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