001package org.hl7.fhir.r4.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.exceptions.FHIRException; 011import org.hl7.fhir.exceptions.PathEngineException; 012import org.hl7.fhir.instance.model.api.IBase; 013import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; 014import org.hl7.fhir.r4.model.Base; 015import org.hl7.fhir.r4.model.ExpressionNode; 016import org.hl7.fhir.r4.model.IdType; 017import org.hl7.fhir.r4.model.TypeDetails; 018import org.hl7.fhir.r4.model.ValueSet; 019import org.hl7.fhir.r4.utils.FHIRPathEngine; 020import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FunctionDetails; 021 022import java.util.List; 023import java.util.Optional; 024 025public class FhirPathR4 implements IFhirPath { 026 027 private final FHIRPathEngine myEngine; 028 029 public FhirPathR4(FhirContext theCtx) { 030 IValidationSupport validationSupport = theCtx.getValidationSupport(); 031 myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); 032 // These changes are to make the FP evaluation non-strict 033 myEngine.setDoNotEnforceAsCaseSensitive(true); 034 myEngine.setDoNotEnforceAsSingletonRule(true); 035 } 036 037 @SuppressWarnings("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(2409) + 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(255) + e.getMessage(), e); 065 } 066 067 for (IBase next : result) { 068 if (!theReturnType.isAssignableFrom(next.getClass())) { 069 throw new FhirPathExecutionException(Msg.code(256) + "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(Object appContext, String name, boolean beforeContext) 098 throws PathEngineException { 099 return null; 100 } 101 102 @Override 103 public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { 104 return null; 105 } 106 107 @Override 108 public boolean log(String argument, List<Base> focus) { 109 return false; 110 } 111 112 @Override 113 public FunctionDetails resolveFunction(String functionName) { 114 return null; 115 } 116 117 @Override 118 public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) 119 throws PathEngineException { 120 return null; 121 } 122 123 @Override 124 public List<Base> executeFunction( 125 Object appContext, List<Base> focus, String functionName, List<List<Base>> parameters) { 126 return null; 127 } 128 129 @Override 130 public Base resolveReference(Object appContext, String theUrl, Base theRefContext) throws FHIRException { 131 return (Base) theEvaluationContext.resolveReference(new IdType(theUrl), theRefContext); 132 } 133 134 @Override 135 public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { 136 return false; 137 } 138 139 @Override 140 public ValueSet resolveValueSet(Object appContext, String url) { 141 return null; 142 } 143 }); 144 } 145 146 private static class ParsedExpression implements IParsedExpression { 147 148 private final ExpressionNode myParsedExpression; 149 150 public ParsedExpression(ExpressionNode theParsedExpression) { 151 myParsedExpression = theParsedExpression; 152 } 153 } 154}