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