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.TypeDetails; 017import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; 018import org.hl7.fhir.r5.model.Base; 019import org.hl7.fhir.r5.model.IdType; 020import org.hl7.fhir.r5.model.ValueSet; 021 022import java.util.Collections; 023import java.util.List; 024import java.util.Optional; 025import java.util.stream.Collectors; 026 027public class FhirPathR5 implements IFhirPath { 028 029 private final FHIRPathEngine myEngine; 030 031 public FhirPathR5(FhirContext theCtx) { 032 IValidationSupport validationSupport = theCtx.getValidationSupport(); 033 myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); 034 myEngine.setDoNotEnforceAsSingletonRule(true); 035 } 036 037 @SuppressWarnings({"unchecked", "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(2411) + 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(198) + e); 065 } 066 067 for (IBase next : result) { 068 if (!theReturnType.isAssignableFrom(next.getClass())) { 069 throw new FhirPathExecutionException(Msg.code(199) + "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( 098 FHIRPathEngine engine, 099 Object appContext, 100 String name, 101 boolean beforeContext, 102 boolean explicitConstant) 103 throws PathEngineException { 104 105 return Collections.unmodifiableList( 106 theEvaluationContext.resolveConstant(appContext, name, beforeContext).stream() 107 .map(Base.class::cast) 108 .collect(Collectors.toList())); 109 } 110 111 @Override 112 public TypeDetails resolveConstantType( 113 FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) 114 throws PathEngineException { 115 return null; 116 } 117 118 @Override 119 public boolean log(String argument, List<Base> focus) { 120 return false; 121 } 122 123 @Override 124 public FHIRPathUtilityClasses.FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { 125 return null; 126 } 127 128 @Override 129 public TypeDetails checkFunction( 130 FHIRPathEngine engine, 131 Object appContext, 132 String functionName, 133 TypeDetails focus, 134 List<TypeDetails> parameters) 135 throws PathEngineException { 136 return null; 137 } 138 139 @Override 140 public List<Base> executeFunction( 141 FHIRPathEngine engine, 142 Object appContext, 143 List<Base> focus, 144 String functionName, 145 List<List<Base>> parameters) { 146 return null; 147 } 148 149 @Override 150 public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) 151 throws FHIRException { 152 return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext); 153 } 154 155 @Override 156 public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) 157 throws FHIRException { 158 return false; 159 } 160 161 @Override 162 public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { 163 return null; 164 } 165 166 @Override 167 public boolean paramIsType(String name, int index) { 168 return false; 169 } 170 }); 171 } 172 173 private static class ParsedExpression implements IParsedExpression { 174 175 private final ExpressionNode myParsedExpression; 176 177 public ParsedExpression(ExpressionNode theParsedExpression) { 178 myParsedExpression = theParsedExpression; 179 } 180 } 181}