
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.fhirpath.ExpressionNode; 014import org.hl7.fhir.r4.fhirpath.FHIRPathEngine; 015import org.hl7.fhir.r4.fhirpath.FHIRPathUtilityClasses.FunctionDetails; 016import org.hl7.fhir.r4.fhirpath.IHostApplicationServices; 017import org.hl7.fhir.r4.fhirpath.TypeDetails; 018import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; 019import org.hl7.fhir.r4.model.Base; 020import org.hl7.fhir.r4.model.IdType; 021import org.hl7.fhir.r4.model.ValueSet; 022import org.hl7.fhir.utilities.fhirpath.FHIRPathConstantEvaluationMode; 023 024import java.util.List; 025import java.util.Optional; 026 027public class FhirPathR4 implements IFhirPath { 028 029 private final FHIRPathEngine myEngine; 030 031 public FhirPathR4(FhirContext theCtx) { 032 IValidationSupport validationSupport = theCtx.getValidationSupport(); 033 myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); 034 // These changes are to make the FP evaluation non-strict 035 myEngine.setDoNotEnforceAsCaseSensitive(true); 036 myEngine.setDoNotEnforceAsSingletonRule(true); 037 } 038 039 @SuppressWarnings("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(2409) + e, 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(255) + e.getMessage(), e); 067 } 068 069 for (IBase next : result) { 070 if (!theReturnType.isAssignableFrom(next.getClass())) { 071 throw new FhirPathExecutionException(Msg.code(256) + "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 return null; 103 } 104 105 @Override 106 public TypeDetails resolveConstantType( 107 FHIRPathEngine engine, Object appContext, String name, FHIRPathConstantEvaluationMode mode) 108 throws PathEngineException { 109 return null; 110 } 111 112 @Override 113 public boolean log(String argument, List<Base> focus) { 114 return false; 115 } 116 117 @Override 118 public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { 119 return null; 120 } 121 122 @Override 123 public TypeDetails checkFunction( 124 FHIRPathEngine engine, 125 Object appContext, 126 String functionName, 127 TypeDetails focus, 128 List<TypeDetails> parameters) 129 throws PathEngineException { 130 return null; 131 } 132 133 @Override 134 public List<Base> executeFunction( 135 FHIRPathEngine engine, 136 Object appContext, 137 List<Base> focus, 138 String functionName, 139 List<List<Base>> parameters) { 140 return null; 141 } 142 143 @Override 144 public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) 145 throws FHIRException { 146 return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext); 147 } 148 149 @Override 150 public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) 151 throws FHIRException { 152 return false; 153 } 154 155 @Override 156 public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { 157 return null; 158 } 159 160 @Override 161 public boolean paramIsType(String name, int index) { 162 return false; 163 } 164 }); 165 } 166 167 private static class ParsedExpression implements IParsedExpression { 168 169 private final ExpressionNode myParsedExpression; 170 171 public ParsedExpression(ExpressionNode theParsedExpression) { 172 myParsedExpression = theParsedExpression; 173 } 174 } 175}