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.hapi.ctx.HapiWorkerContext;
014import org.hl7.fhir.r5.model.Base;
015import org.hl7.fhir.r5.model.ExpressionNode;
016import org.hl7.fhir.r5.model.IdType;
017import org.hl7.fhir.r5.model.TypeDetails;
018import org.hl7.fhir.r5.model.ValueSet;
019import org.hl7.fhir.r5.utils.FHIRPathEngine;
020import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses;
021
022import java.util.List;
023import java.util.Optional;
024
025public class FhirPathR5 implements IFhirPath {
026
027        private final FHIRPathEngine myEngine;
028
029        public FhirPathR5(FhirContext theCtx) {
030                IValidationSupport validationSupport = theCtx.getValidationSupport();
031                myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport));
032                myEngine.setDoNotEnforceAsSingletonRule(true);
033        }
034
035        @SuppressWarnings({"unchecked", "unchecked"})
036        @Override
037        public <T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType) {
038                ExpressionNode parsed;
039                try {
040                        parsed = myEngine.parse(thePath);
041                } catch (FHIRException e) {
042                        throw new FhirPathExecutionException(Msg.code(2411) + e);
043                }
044                return (List<T>) evaluate(theInput, parsed, theReturnType);
045        }
046
047        @SuppressWarnings("unchecked")
048        @Override
049        public <T extends IBase> List<T> evaluate(
050                        IBase theInput, IParsedExpression theParsedExpression, Class<T> theReturnType) {
051                ExpressionNode expressionNode = ((ParsedExpression) theParsedExpression).myParsedExpression;
052                return (List<T>) evaluate(theInput, expressionNode, theReturnType);
053        }
054
055        @Nonnull
056        private <T extends IBase> List<Base> evaluate(
057                        IBase theInput, ExpressionNode expressionNode, Class<T> theReturnType) {
058                List<Base> result;
059                try {
060                        result = myEngine.evaluate((Base) theInput, expressionNode);
061                } catch (FHIRException e) {
062                        throw new FhirPathExecutionException(Msg.code(198) + e);
063                }
064
065                for (IBase next : result) {
066                        if (!theReturnType.isAssignableFrom(next.getClass())) {
067                                throw new FhirPathExecutionException(Msg.code(199) + "FhirPath expression returned unexpected type "
068                                                + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName());
069                        }
070                }
071                return result;
072        }
073
074        @Override
075        public <T extends IBase> Optional<T> evaluateFirst(IBase theInput, String thePath, Class<T> theReturnType) {
076                return evaluate(theInput, thePath, theReturnType).stream().findFirst();
077        }
078
079        @Override
080        public <T extends IBase> Optional<T> evaluateFirst(
081                        IBase theInput, IParsedExpression theParsedExpression, Class<T> theReturnType) {
082                return evaluate(theInput, theParsedExpression, theReturnType).stream().findFirst();
083        }
084
085        @Override
086        public IParsedExpression parse(String theExpression) {
087                return new ParsedExpression(myEngine.parse(theExpression));
088        }
089
090        @Override
091        public void setEvaluationContext(@Nonnull IFhirPathEvaluationContext theEvaluationContext) {
092                myEngine.setHostServices(new FHIRPathEngine.IEvaluationContext() {
093
094                        @Override
095                        public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext)
096                                        throws PathEngineException {
097                                return null;
098                        }
099
100                        @Override
101                        public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
102                                return null;
103                        }
104
105                        @Override
106                        public boolean log(String argument, List<Base> focus) {
107                                return false;
108                        }
109
110                        @Override
111                        public FHIRPathUtilityClasses.FunctionDetails resolveFunction(String functionName) {
112                                return null;
113                        }
114
115                        @Override
116                        public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters)
117                                        throws PathEngineException {
118                                return null;
119                        }
120
121                        @Override
122                        public List<Base> executeFunction(
123                                        Object appContext, List<Base> focus, String functionName, List<List<Base>> parameters) {
124                                return null;
125                        }
126
127                        @Override
128                        public Base resolveReference(Object appContext, String theUrl, Base refContext) throws FHIRException {
129                                return (Base) theEvaluationContext.resolveReference(new IdType(theUrl), refContext);
130                        }
131
132                        @Override
133                        public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
134                                return false;
135                        }
136
137                        @Override
138                        public ValueSet resolveValueSet(Object appContext, String url) {
139                                return null;
140                        }
141                });
142        }
143
144        private static class ParsedExpression implements IParsedExpression {
145
146                private final ExpressionNode myParsedExpression;
147
148                public ParsedExpression(ExpressionNode theParsedExpression) {
149                        myParsedExpression = theParsedExpression;
150                }
151        }
152}