001package org.hl7.fhir.r5.hapi.fhirpath;
002
003import ca.uhn.fhir.context.FhirContext;
004import ca.uhn.fhir.context.support.IValidationSupport;
005import ca.uhn.fhir.fhirpath.FhirPathExecutionException;
006import ca.uhn.fhir.fhirpath.IFhirPath;
007import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext;
008import ca.uhn.fhir.i18n.Msg;
009import jakarta.annotation.Nonnull;
010import org.hl7.fhir.exceptions.FHIRException;
011import org.hl7.fhir.exceptions.PathEngineException;
012import org.hl7.fhir.instance.model.api.IBase;
013import org.hl7.fhir.r5.fhirpath.ExpressionNode;
014import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
015import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses;
016import org.hl7.fhir.r5.fhirpath.TypeDetails;
017import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
018import org.hl7.fhir.r5.model.Base;
019import org.hl7.fhir.r5.model.IdType;
020import org.hl7.fhir.r5.model.ValueSet;
021
022import java.util.Collections;
023import java.util.List;
024import java.util.Optional;
025import java.util.stream.Collectors;
026
027public class FhirPathR5 implements IFhirPath {
028
029        private final FHIRPathEngine myEngine;
030
031        public FhirPathR5(FhirContext theCtx) {
032                IValidationSupport validationSupport = theCtx.getValidationSupport();
033                myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport));
034                myEngine.setDoNotEnforceAsSingletonRule(true);
035        }
036
037        @SuppressWarnings({"unchecked", "unchecked"})
038        @Override
039        public <T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType) {
040                ExpressionNode parsed;
041                try {
042                        parsed = myEngine.parse(thePath);
043                } catch (FHIRException e) {
044                        throw new FhirPathExecutionException(Msg.code(2411) + e);
045                }
046                return (List<T>) evaluate(theInput, parsed, theReturnType);
047        }
048
049        @SuppressWarnings("unchecked")
050        @Override
051        public <T extends IBase> List<T> evaluate(
052                        IBase theInput, IParsedExpression theParsedExpression, Class<T> theReturnType) {
053                ExpressionNode expressionNode = ((ParsedExpression) theParsedExpression).myParsedExpression;
054                return (List<T>) evaluate(theInput, expressionNode, theReturnType);
055        }
056
057        @Nonnull
058        private <T extends IBase> List<Base> evaluate(
059                        IBase theInput, ExpressionNode expressionNode, Class<T> theReturnType) {
060                List<Base> result;
061                try {
062                        result = myEngine.evaluate((Base) theInput, expressionNode);
063                } catch (FHIRException e) {
064                        throw new FhirPathExecutionException(Msg.code(198) + e);
065                }
066
067                for (IBase next : result) {
068                        if (!theReturnType.isAssignableFrom(next.getClass())) {
069                                throw new FhirPathExecutionException(Msg.code(199) + "FhirPath expression returned unexpected type "
070                                                + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName());
071                        }
072                }
073                return result;
074        }
075
076        @Override
077        public <T extends IBase> Optional<T> evaluateFirst(IBase theInput, String thePath, Class<T> theReturnType) {
078                return evaluate(theInput, thePath, theReturnType).stream().findFirst();
079        }
080
081        @Override
082        public <T extends IBase> Optional<T> evaluateFirst(
083                        IBase theInput, IParsedExpression theParsedExpression, Class<T> theReturnType) {
084                return evaluate(theInput, theParsedExpression, theReturnType).stream().findFirst();
085        }
086
087        @Override
088        public IParsedExpression parse(String theExpression) {
089                return new ParsedExpression(myEngine.parse(theExpression));
090        }
091
092        @Override
093        public void setEvaluationContext(@Nonnull IFhirPathEvaluationContext theEvaluationContext) {
094                myEngine.setHostServices(new FHIRPathEngine.IEvaluationContext() {
095
096                        @Override
097                        public List<Base> resolveConstant(
098                                        FHIRPathEngine engine,
099                                        Object appContext,
100                                        String name,
101                                        boolean beforeContext,
102                                        boolean explicitConstant)
103                                        throws PathEngineException {
104
105                                return Collections.unmodifiableList(
106                                                theEvaluationContext.resolveConstant(appContext, name, beforeContext).stream()
107                                                                .map(Base.class::cast)
108                                                                .collect(Collectors.toList()));
109                        }
110
111                        @Override
112                        public TypeDetails resolveConstantType(
113                                        FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant)
114                                        throws PathEngineException {
115                                return null;
116                        }
117
118                        @Override
119                        public boolean log(String argument, List<Base> focus) {
120                                return false;
121                        }
122
123                        @Override
124                        public FHIRPathUtilityClasses.FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) {
125                                return null;
126                        }
127
128                        @Override
129                        public TypeDetails checkFunction(
130                                        FHIRPathEngine engine,
131                                        Object appContext,
132                                        String functionName,
133                                        TypeDetails focus,
134                                        List<TypeDetails> parameters)
135                                        throws PathEngineException {
136                                return null;
137                        }
138
139                        @Override
140                        public List<Base> executeFunction(
141                                        FHIRPathEngine engine,
142                                        Object appContext,
143                                        List<Base> focus,
144                                        String functionName,
145                                        List<List<Base>> parameters) {
146                                return null;
147                        }
148
149                        @Override
150                        public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext)
151                                        throws FHIRException {
152                                return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext);
153                        }
154
155                        @Override
156                        public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url)
157                                        throws FHIRException {
158                                return false;
159                        }
160
161                        @Override
162                        public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) {
163                                return null;
164                        }
165
166                        @Override
167                        public boolean paramIsType(String name, int index) {
168                                return false;
169                        }
170                });
171        }
172
173        private static class ParsedExpression implements IParsedExpression {
174
175                private final ExpressionNode myParsedExpression;
176
177                public ParsedExpression(ExpressionNode theParsedExpression) {
178                        myParsedExpression = theParsedExpression;
179                }
180        }
181}