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