From 40c996828a2690943a270eea1f8273c83353e3b7 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 23 Oct 2023 10:24:54 +1100 Subject: [PATCH 1/8] Revise FHIRPath API so hosts can evaluate expressions in custom functions --- .../org/hl7/fhir/r4/utils/FHIRPathEngine.java | 62 +++++++++---------- .../org/hl7/fhir/r4/utils/LiquidEngine.java | 32 +++++----- .../fhir/r4/utils/StructureMapUtilities.java | 16 ++--- .../org/hl7/fhir/r4/test/FHIRPathTests.java | 18 +++--- .../fhir/r4/test/SnapShotGenerationTests.java | 16 ++--- .../r4b/comparison/ComparisonRenderer.java | 17 ++--- .../hl7/fhir/r4b/utils/FHIRPathEngine.java | 60 +++++++++--------- .../org/hl7/fhir/r4b/utils/LiquidEngine.java | 32 +++++----- .../structuremap/FHIRPathHostServices.java | 16 ++--- .../structuremap/StructureMapUtilities.java | 16 ++--- .../org/hl7/fhir/r4b/test/FHIRPathTests.java | 18 +++--- .../r4b/test/SnapShotGenerationTests.java | 16 ++--- .../r5/comparison/ComparisonRenderer.java | 17 ++--- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 56 ++++++++--------- .../org/hl7/fhir/r5/utils/LiquidEngine.java | 32 +++++----- .../org/hl7/fhir/r5/utils/sql/Runner.java | 16 ++--- .../structuremap/FHIRPathHostServices.java | 16 ++--- .../org/hl7/fhir/r5/test/FHIRPathTests.java | 18 +++--- .../fhir/r5/test/SnapShotGenerationTests.java | 16 ++--- .../FHIRPathHostServicesTest.java | 2 +- .../tests/SnapShotGenerationXTests.java | 16 ++--- .../validation/tests/ValidationTests.java | 16 ++--- 22 files changed, 263 insertions(+), 261 deletions(-) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java index 4a7dc0c14..d493cf8cf 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java @@ -59,6 +59,7 @@ import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FHIRConstant; import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.ClassTypeInfo; import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.TypedElementDefinition; +import org.hl7.fhir.r4.utils.FHIRPathEngine; import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FunctionDetails; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MergedList; @@ -150,9 +151,9 @@ public class FHIRPathEngine { * @return the value of the reference (or null, if it's not valid, though can * throw an exception if desired) */ - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException; + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException; - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException; + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException; /** * when the .log() function is called @@ -169,7 +170,7 @@ public class FHIRPathEngine { * @param functionName * @return null if the function is not known */ - public FunctionDetails resolveFunction(String functionName); + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName); /** * Check the function parameters, and throw an error if they are incorrect, or @@ -179,7 +180,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException; /** @@ -188,7 +189,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters); /** @@ -200,14 +201,14 @@ public class FHIRPathEngine { * @return * @throws FHIRException */ - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException; + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException; - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException; + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException; /* * return the value set referenced by the url, which has been used in memberOf() */ - public ValueSet resolveValueSet(Object appContext, String url); + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url); } /** @@ -1032,7 +1033,7 @@ public class FHIRPathEngine { FunctionDetails details = null; if (f == null) { if (hostServices != null) { - details = hostServices.resolveFunction(result.getName()); + details = hostServices.resolveFunction(this, result.getName()); } if (details == null) { throw lexer.error("The name " + result.getName() + " is not a valid function name"); @@ -1473,7 +1474,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - work.addAll(resolveConstant(context, exp.getConstant(), false, exp)); + work.addAll(resolveConstant(context, exp.getConstant(), false, exp, true)); break; case Group: work2 = execute(context, focus, exp.getGroup(), atEntry); @@ -1563,7 +1564,7 @@ public class FHIRPathEngine { } else if (atEntry && exp.getName().equals("$index")) { result.addType(TypeDetails.FP_Integer); } else if (atEntry && focus == null) { - result.update(executeContextType(context, exp.getName(), exp)); + result.update(executeContextType(context, exp.getName(), exp, false)); } else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); @@ -1582,7 +1583,7 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Quantity); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant(), exp)); + result.update(resolveConstantType(context, exp.getConstant(), exp, true)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1612,8 +1613,7 @@ public class FHIRPathEngine { return result; } - private List resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, - ExpressionNode expr) throws PathEngineException { + private List resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (constant == null) { return new ArrayList(); } @@ -1622,7 +1622,7 @@ public class FHIRPathEngine { } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext, expr); + return resolveConstant(context, c.getValue(), beforeContext, expr, explicitConstant); } else if (c.getValue().startsWith("@")) { return new ArrayList(Arrays.asList(processDateConstant(context.appInfo, c.getValue().substring(1), expr))); } else { @@ -1692,7 +1692,7 @@ public class FHIRPathEngine { } } - private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) + private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.equals("%sct")) { return new ArrayList(Arrays.asList(new StringType("http://snomed.info/sct").noExtensions())); @@ -1726,7 +1726,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); + return hostServices.resolveConstant(this, context.appInfo, s.substring(1), beforeContext, explicitConstant); } } @@ -2549,7 +2549,7 @@ public class FHIRPathEngine { throws FHIRException { boolean ans = false; String url = right.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs != null) { for (Base l : left) { @@ -3079,7 +3079,7 @@ public class FHIRPathEngine { return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (constant instanceof BooleanType) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -3090,7 +3090,7 @@ public class FHIRPathEngine { } else if (constant instanceof Quantity) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } else if (constant instanceof FHIRConstant) { - return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr, explicitConstant); } else if (constant == null) { return new TypeDetails(CollectionStatus.SINGLETON); } else { @@ -3098,7 +3098,7 @@ public class FHIRPathEngine { } } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) { @@ -3137,7 +3137,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstantType(context.appInfo, s); + return hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant); } } @@ -3146,7 +3146,7 @@ public class FHIRPathEngine { List result = new ArrayList(); if (atEntry && context.appInfo != null && hostServices != null) { // we'll see if the name matches a constant known by the context. - List temp = hostServices.resolveConstant(context.appInfo, exp.getName(), true); + List temp = hostServices.resolveConstant(this, context.appInfo, exp.getName(), true, false); if (!temp.isEmpty()) { result.addAll(temp); return result; @@ -3177,7 +3177,7 @@ public class FHIRPathEngine { // constant known by the context. // (if the name does match, and the user wants to get the constant value, // they'll have to try harder... - result.addAll(hostServices.resolveConstant(context.appInfo, exp.getName(), false)); + result.addAll(hostServices.resolveConstant(this, context.appInfo, exp.getName(), false, false)); } return result; } @@ -3186,12 +3186,12 @@ public class FHIRPathEngine { return null; } - private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr, boolean explicitConstant) throws PathEngineException, DefinitionException { if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); } - return hostServices.resolveConstantType(context.appInfo, name); + return hostServices.resolveConstantType(this, context.appInfo, name, explicitConstant); } private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry) @@ -3607,7 +3607,7 @@ public class FHIRPathEngine { } case Custom: { - return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes); + return hostServices.checkFunction(this, context.appInfo, exp.getName(), focus, paramTypes); } default: break; @@ -3932,7 +3932,7 @@ public class FHIRPathEngine { for (ExpressionNode p : exp.getParameters()) { params.add(execute(context, focus, p, true)); } - return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); + return hostServices.executeFunction(this, context.appInfo, focus, exp.getName(), params); } default: throw new Error("not Implemented yet"); @@ -4605,7 +4605,7 @@ public class FHIRPathEngine { } String url = nl.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs == null) { return new ArrayList(); @@ -5210,7 +5210,7 @@ public class FHIRPathEngine { } } else if (hostServices != null) { try { - res = hostServices.resolveReference(context.appInfo, s, refContext); + res = hostServices.resolveReference(this, context.appInfo, s, refContext); } catch (Exception e) { res = null; } @@ -5740,7 +5740,7 @@ public class FHIRPathEngine { result.add(new BooleanType(false).noExtensions()); } else { String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); - result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); + result.add(new BooleanType(hostServices.conformsToProfile(this, context.appInfo, focus.get(0), url)).noExtensions()); } return result; } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java index eb73998a9..c4c21baf2 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java @@ -369,21 +369,21 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (ctxt.vars.containsKey(name)) return new ArrayList<>(Arrays.asList(ctxt.vars.get(name))); if (externalHostServices == null) return null; - return externalHostServices.resolveConstant(ctxt.externalContext, name, beforeContext); + return externalHostServices.resolveConstant(engine, ctxt.externalContext, name, beforeContext, explicitConstant); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.resolveConstantType(ctxt.externalContext, name); + return externalHostServices.resolveConstantType(engine, ctxt.externalContext, name, explicitConstant); } @Override @@ -394,51 +394,51 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if (externalHostServices == null) return null; - return externalHostServices.resolveFunction(functionName); + return externalHostServices.resolveFunction(engine, functionName); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.checkFunction(ctxt.externalContext, functionName, parameters); + return externalHostServices.checkFunction(engine, ctxt.externalContext, functionName, focus, parameters); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.executeFunction(ctxt.externalContext, focus, functionName, parameters); + return externalHostServices.executeFunction(engine, ctxt.externalContext, focus, functionName, parameters); } @Override - public Base resolveReference(Object appContext, String url, Base base) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base base) throws FHIRException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return resolveReference(ctxt.externalContext, url, base); + return resolveReference(engine, ctxt.externalContext, url, base); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (externalHostServices == null) return false; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return conformsToProfile(ctxt.externalContext, item, url); + return conformsToProfile(engine, ctxt.externalContext, item, url); } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (externalHostServices != null) - return externalHostServices.resolveValueSet(ctxt.externalContext, url); + return externalHostServices.resolveValueSet(engine, ctxt.externalContext, url); else return engine.getWorker().fetchResource(ValueSet.class, url); } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java index fea611d93..d83edeb05 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java @@ -177,7 +177,7 @@ public class StructureMapUtilities { private class FHIRPathHostServices implements IEvaluationContext { - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { Variables vars = (Variables) appContext; Base res = vars.get(VariableMode.INPUT, name); @@ -190,7 +190,7 @@ public class StructureMapUtilities { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (!(appContext instanceof VariablesForProfiling)) throw new Error( "Internal Logic Error (wrong type '" + appContext.getClass().getName() + "' in resolveConstantType)"); @@ -207,31 +207,31 @@ public class StructureMapUtilities { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; // throw new Error("Not Implemented Yet"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new Error("Not Implemented Yet"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } @Override - public Base resolveReference(Object appContext, String url, Base base) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base base) throws FHIRException { if (services == null) return null; return services.resolveReference(appContext, url); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = worker.newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -245,7 +245,7 @@ public class StructureMapUtilities { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not Implemented Yet"); } diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java index 51e3105ab..e8bbd622e 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java @@ -58,14 +58,14 @@ public class FHIRPathTests { public class FHIRPathTestEvaluationServices implements IEvaluationContext { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveConstant), when item is element"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveConstantType), when item is element"); } @@ -76,34 +76,34 @@ public class FHIRPathTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveFunction), when item is element (for " + functionName + ")"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.checkFunction), when item is element"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element"); } @Override - public Base resolveReference(Object appContext, String url, Base base) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base base) throws FHIRException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveReference), when item is element"); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (url.equals("http://hl7.org/fhir/StructureDefinition/Patient")) return true; if (url.equals("http://hl7.org/fhir/StructureDefinition/Person")) @@ -113,7 +113,7 @@ public class FHIRPathTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return TestingUtilities.context().fetchResource(ValueSet.class, url); } @@ -323,7 +323,7 @@ public class FHIRPathTests { final String DUMMY_CONSTANT_2 = "dummyConstant2"; fp.setHostServices(new FHIRPathTestEvaluationServices() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Arrays.asList(new StringType(DUMMY_CONSTANT_1).noExtensions(), diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java index 3077c83e2..f1a504778 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java @@ -300,13 +300,13 @@ public class SnapShotGenerationTests { // FHIRPath methods @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @@ -317,14 +317,14 @@ public class SnapShotGenerationTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if ("fixture".equals(functionName)) return new FunctionDetails("Access a fixture defined in the testing context", 0, 1); return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if ("fixture".equals(functionName)) return new TypeDetails(CollectionStatus.SINGLETON, TestingUtilities.context().getResourceNamesAsSet()); @@ -332,7 +332,7 @@ public class SnapShotGenerationTests { } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); @@ -348,13 +348,13 @@ public class SnapShotGenerationTests { } @Override - public Base resolveReference(Object appContext, String url, Base base) { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base base) { // TODO Auto-generated method stub return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = TestingUtilities.context().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -381,7 +381,7 @@ public class SnapShotGenerationTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not implemented yet"); } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ComparisonRenderer.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ComparisonRenderer.java index 4a355f1b6..65d4f3d58 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ComparisonRenderer.java @@ -14,6 +14,7 @@ import java.util.Set; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r4b.utils.FHIRPathEngine; import org.hl7.fhir.r4b.comparison.CodeSystemComparer.CodeSystemComparison; import org.hl7.fhir.r4b.comparison.ProfileComparer.ProfileComparison; import org.hl7.fhir.r4b.comparison.ResourceComparer.PlaceHolderComparison; @@ -231,7 +232,7 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { @SuppressWarnings("unchecked") Map vars = (Map) appContext; List res = new ArrayList<>(); @@ -242,7 +243,7 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { @SuppressWarnings("unchecked") Map vars = (Map) appContext; Base b = vars.get(name); @@ -255,34 +256,34 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { return null; } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java index ba05b69e6..99cc200f3 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java @@ -152,9 +152,9 @@ public class FHIRPathEngine { * @return the value of the reference (or null, if it's not valid, though can * throw an exception if desired) */ - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException; + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException; - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException; + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException; /** * when the .log() function is called @@ -171,7 +171,7 @@ public class FHIRPathEngine { * @param functionName * @return null if the function is not known */ - public FunctionDetails resolveFunction(String functionName); + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName); /** * Check the function parameters, and throw an error if they are incorrect, or @@ -181,7 +181,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException; /** @@ -190,7 +190,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters); /** @@ -202,14 +202,14 @@ public class FHIRPathEngine { * @return * @throws FHIRException */ - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException; + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException; - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException; + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException; /* * return the value set referenced by the url, which has been used in memberOf() */ - public ValueSet resolveValueSet(Object appContext, String url); + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url); } /** @@ -1034,7 +1034,7 @@ public class FHIRPathEngine { FunctionDetails details = null; if (f == null) { if (hostServices != null) { - details = hostServices.resolveFunction(result.getName()); + details = hostServices.resolveFunction(this, result.getName()); } if (details == null) { throw lexer.error("The name " + result.getName() + " is not a valid function name"); @@ -1474,7 +1474,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - work.addAll(resolveConstant(context, exp.getConstant(), false, exp)); + work.addAll(resolveConstant(context, exp.getConstant(), false, exp, true)); break; case Group: work2 = execute(context, focus, exp.getGroup(), atEntry); @@ -1564,7 +1564,7 @@ public class FHIRPathEngine { } else if (atEntry && exp.getName().equals("$index")) { result.addType(TypeDetails.FP_Integer); } else if (atEntry && focus == null) { - result.update(executeContextType(context, exp.getName(), exp)); + result.update(executeContextType(context, exp.getName(), exp, false)); } else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); @@ -1583,7 +1583,7 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Quantity); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant(), exp)); + result.update(resolveConstantType(context, exp.getConstant(), exp, true)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1614,7 +1614,7 @@ public class FHIRPathEngine { } private List resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, - ExpressionNode expr) throws PathEngineException { + ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (constant == null) { return new ArrayList(); } @@ -1623,7 +1623,7 @@ public class FHIRPathEngine { } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext, expr); + return resolveConstant(context, c.getValue(), beforeContext, expr, explicitConstant); } else if (c.getValue().startsWith("@")) { return new ArrayList(Arrays.asList(processDateConstant(context.appInfo, c.getValue().substring(1), expr))); } else { @@ -1693,7 +1693,7 @@ public class FHIRPathEngine { } } - private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) + private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.equals("%sct")) { return new ArrayList(Arrays.asList(new StringType("http://snomed.info/sct").noExtensions())); @@ -1727,7 +1727,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); + return hostServices.resolveConstant(this, context.appInfo, s.substring(1), beforeContext, explicitConstant); } } @@ -2550,7 +2550,7 @@ public class FHIRPathEngine { throws FHIRException { boolean ans = false; String url = right.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs != null) { for (Base l : left) { @@ -3081,7 +3081,7 @@ public class FHIRPathEngine { return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (constant instanceof BooleanType) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -3092,7 +3092,7 @@ public class FHIRPathEngine { } else if (constant instanceof Quantity) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } else if (constant instanceof FHIRConstant) { - return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr, explicitConstant); } else if (constant == null) { return new TypeDetails(CollectionStatus.SINGLETON); } else { @@ -3100,7 +3100,7 @@ public class FHIRPathEngine { } } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) { @@ -3139,7 +3139,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstantType(context.appInfo, s); + return hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant); } } @@ -3148,7 +3148,7 @@ public class FHIRPathEngine { List result = new ArrayList(); if (atEntry && context.appInfo != null && hostServices != null) { // we'll see if the name matches a constant known by the context. - List temp = hostServices.resolveConstant(context.appInfo, exp.getName(), true); + List temp = hostServices.resolveConstant(this, context.appInfo, exp.getName(), true, false); if (!temp.isEmpty()) { result.addAll(temp); return result; @@ -3179,7 +3179,7 @@ public class FHIRPathEngine { // constant known by the context. // (if the name does match, and the user wants to get the constant value, // they'll have to try harder... - result.addAll(hostServices.resolveConstant(context.appInfo, exp.getName(), false)); + result.addAll(hostServices.resolveConstant(this, context.appInfo, exp.getName(), false, false)); } return result; } @@ -3188,12 +3188,12 @@ public class FHIRPathEngine { return null; } - private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr, boolean explicitConstant) throws PathEngineException, DefinitionException { if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); } - return hostServices.resolveConstantType(context.appInfo, name); + return hostServices.resolveConstantType(this, context.appInfo, name, explicitConstant); } private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry) @@ -3609,7 +3609,7 @@ public class FHIRPathEngine { } case Custom: { - return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes); + return hostServices.checkFunction(this, context.appInfo, exp.getName(), focus, paramTypes); } default: break; @@ -3934,7 +3934,7 @@ public class FHIRPathEngine { for (ExpressionNode p : exp.getParameters()) { params.add(execute(context, focus, p, true)); } - return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); + return hostServices.executeFunction(this, context.appInfo, focus, exp.getName(), params); } default: throw new Error("not Implemented yet"); @@ -4607,7 +4607,7 @@ public class FHIRPathEngine { } String url = nl.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs == null) { return new ArrayList(); @@ -5213,7 +5213,7 @@ public class FHIRPathEngine { } } else if (hostServices != null) { try { - res = hostServices.resolveReference(context.appInfo, s, refContext); + res = hostServices.resolveReference(this, context.appInfo, s, refContext); } catch (Exception e) { res = null; } @@ -5743,7 +5743,7 @@ public class FHIRPathEngine { result.add(new BooleanType(false).noExtensions()); } else { String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); - result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); + result.add(new BooleanType(hostServices.conformsToProfile(this, context.appInfo, focus.get(0), url)).noExtensions()); } return result; } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/LiquidEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/LiquidEngine.java index 5a94f53af..b07fa8c79 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/LiquidEngine.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/LiquidEngine.java @@ -738,7 +738,7 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (ctxt.loopVars.containsKey(name)) return new ArrayList(Arrays.asList(ctxt.loopVars.get(name))); @@ -746,15 +746,15 @@ public class LiquidEngine implements IEvaluationContext { return new ArrayList(Arrays.asList(ctxt.globalVars.get(name))); if (externalHostServices == null) return new ArrayList(); - return externalHostServices.resolveConstant(ctxt.externalContext, name, beforeContext); + return externalHostServices.resolveConstant(engine, ctxt.externalContext, name, beforeContext, explicitConstant); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.resolveConstantType(ctxt.externalContext, name); + return externalHostServices.resolveConstantType(engine, ctxt.externalContext, name, explicitConstant); } @Override @@ -765,49 +765,49 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if (externalHostServices == null) return null; - return externalHostServices.resolveFunction(functionName); + return externalHostServices.resolveFunction(engine, functionName); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.checkFunction(ctxt.externalContext, functionName, parameters); + return externalHostServices.checkFunction(engine, ctxt.externalContext, functionName, focus, parameters); } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.executeFunction(ctxt.externalContext, focus, functionName, parameters); + return externalHostServices.executeFunction(engine, ctxt.externalContext, focus, functionName, parameters); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return resolveReference(ctxt.externalContext, url, refContext); + return resolveReference(engine, ctxt.externalContext, url, refContext); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (externalHostServices == null) return false; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return conformsToProfile(ctxt.externalContext, item, url); + return conformsToProfile(engine, ctxt.externalContext, item, url); } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (externalHostServices != null) - return externalHostServices.resolveValueSet(ctxt.externalContext, url); + return externalHostServices.resolveValueSet(engine, ctxt.externalContext, url); else return engine.getWorker().fetchResource(ValueSet.class, url); } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/FHIRPathHostServices.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/FHIRPathHostServices.java index 76f27269e..a779f7786 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/FHIRPathHostServices.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/FHIRPathHostServices.java @@ -24,7 +24,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { this.structureMapUtilities = structureMapUtilities; } - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { Variables vars = (Variables) appContext; Base res = vars.get(VariableMode.INPUT, name); if (res == null) @@ -36,7 +36,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (!(appContext instanceof VariablesForProfiling)) throw new Error( "Internal Logic Error (wrong type '" + appContext.getClass().getName() + "' in resolveConstantType)"); @@ -53,24 +53,24 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; // throw new Error("Not Implemented Yet"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new Error("Not Implemented Yet"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { if (structureMapUtilities.getServices() == null) return null; return structureMapUtilities.getServices().resolveReference(appContext, url); @@ -84,7 +84,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = structureMapUtilities.getWorker().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -100,7 +100,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not Implemented Yet"); } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/StructureMapUtilities.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/StructureMapUtilities.java index bb60ace80..208182c96 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/StructureMapUtilities.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/structuremap/StructureMapUtilities.java @@ -111,7 +111,7 @@ public class StructureMapUtilities { private class FHIRPathHostServices implements IEvaluationContext { - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { Variables vars = (Variables) appContext; List list = new ArrayList(); @@ -126,7 +126,7 @@ public class StructureMapUtilities { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (!(appContext instanceof VariablesForProfiling)) throw new Error( "Internal Logic Error (wrong type '" + appContext.getClass().getName() + "' in resolveConstantType)"); @@ -143,31 +143,31 @@ public class StructureMapUtilities { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; // throw new Error("Not Implemented Yet"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new Error("Not Implemented Yet"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } @Override - public Base resolveReference(Object appContext, String url, Base base) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base base) throws FHIRException { if (services == null) return null; return services.resolveReference(appContext, url); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = worker.newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -181,7 +181,7 @@ public class StructureMapUtilities { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not Implemented Yet"); } diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java index b600f79e1..fbb32f2dd 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java @@ -44,14 +44,14 @@ public class FHIRPathTests { public class FHIRPathTestEvaluationServices implements IEvaluationContext { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveConstant), when item is element"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveConstantType), when item is element"); } @@ -62,34 +62,34 @@ public class FHIRPathTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveFunction), when item is element (for " + functionName + ")"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.checkFunction), when item is element"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element"); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { throw new NotImplementedException( "Not done yet (FHIRPathTestEvaluationServices.resolveReference), when item is element"); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (url.equals("http://hl7.org/fhir/StructureDefinition/Patient")) return true; if (url.equals("http://hl7.org/fhir/StructureDefinition/Person")) @@ -99,7 +99,7 @@ public class FHIRPathTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return TestingUtilities.context().fetchResource(ValueSet.class, url); } @@ -310,7 +310,7 @@ public class FHIRPathTests { final String DUMMY_CONSTANT_2 = "dummyConstant2"; fp.setHostServices(new FHIRPathTestEvaluationServices() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Arrays.asList(new StringType(DUMMY_CONSTANT_1).noExtensions(), diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java index 6ef70764f..2e3a2b580 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java @@ -368,13 +368,13 @@ public class SnapShotGenerationTests { // FHIRPath methods @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @@ -385,14 +385,14 @@ public class SnapShotGenerationTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if ("fixture".equals(functionName)) return new FunctionDetails("Access a fixture defined in the testing context", 0, 1); return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if ("fixture".equals(functionName)) return new TypeDetails(CollectionStatus.SINGLETON, TestingUtilities.context().getResourceNamesAsSet()); @@ -400,7 +400,7 @@ public class SnapShotGenerationTests { } @Override - public List executeFunction(Object appContext, List focus, String functionName, + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); @@ -416,13 +416,13 @@ public class SnapShotGenerationTests { } @Override - public Base resolveReference(Object appContext, String url, Base refContext) { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) { // TODO Auto-generated method stub return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = TestingUtilities.context().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -451,7 +451,7 @@ public class SnapShotGenerationTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not implemented yet"); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index 71963825a..9a3bd1882 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -14,6 +14,7 @@ import java.util.Set; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison; import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; import org.hl7.fhir.r5.comparison.ResourceComparer.PlaceHolderComparison; @@ -255,7 +256,7 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { @SuppressWarnings("unchecked") Map vars = (Map) appContext; List res = new ArrayList<>(); @@ -266,7 +267,7 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { @SuppressWarnings("unchecked") Map vars = (Map) appContext; Base b = vars.get(name); @@ -279,32 +280,32 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { return null; } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 5def56759..b51bc171d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -153,8 +153,8 @@ public class FHIRPathEngine { * @param beforeContext - whether this is being called before the name is resolved locally, or not * @return the value of the reference (or null, if it's not valid, though can throw an exception if desired) */ - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException; - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException; + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException; + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException; /** * when the .log() function is called @@ -171,7 +171,7 @@ public class FHIRPathEngine { * @param functionName * @return null if the function is not known */ - public FunctionDetails resolveFunction(String functionName); + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName); /** * Check the function parameters, and throw an error if they are incorrect, or return the type for the function @@ -179,7 +179,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException; + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException; /** * @param appContext @@ -187,7 +187,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public List executeFunction(Object appContext, List focus, String functionName, List> parameters); + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters); /** * Implementation of resolve() function. Passed a string, return matching resource, if one is known - else null @@ -196,14 +196,14 @@ public class FHIRPathEngine { * @return * @throws FHIRException */ - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException; + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException; - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException; + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException; /* * return the value set referenced by the url, which has been used in memberOf() */ - public ValueSet resolveValueSet(Object appContext, String url); + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url); /** * For the moment, there can only be one parameter if it's a type parameter @@ -1112,7 +1112,7 @@ public class FHIRPathEngine { FunctionDetails details = null; if (f == null) { if (hostServices != null) { - details = hostServices.resolveFunction(result.getName()); + details = hostServices.resolveFunction(this, result.getName()); } if (details == null) { throw lexer.error("The name "+result.getName()+" is not a valid function name"); @@ -1536,7 +1536,7 @@ public class FHIRPathEngine { } else if (atEntry && exp.getName().equals("$index")) { result.addType(TypeDetails.FP_Integer); } else if (atEntry && focus == null) { - result.update(executeContextType(context, exp.getName(), exp)); + result.update(executeContextType(context, exp.getName(), exp, false)); } else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry, focus, elementDependencies)); @@ -1560,7 +1560,7 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Quantity); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant(), exp)); + result.update(resolveConstantType(context, exp.getConstant(), exp, true)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), elementDependencies, atEntry, canBeNone, exp)); @@ -1612,7 +1612,7 @@ public class FHIRPathEngine { } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext, expr); + return resolveConstant(context, c.getValue(), beforeContext, expr, true); } else if (c.getValue().startsWith("@")) { return new ArrayList(Arrays.asList(processDateConstant(context.appInfo, c.getValue().substring(1), expr))); } else { @@ -1683,7 +1683,7 @@ public class FHIRPathEngine { } - private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { + private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.equals("%sct")) { return new ArrayList(Arrays.asList(new StringType("http://snomed.info/sct").noExtensions())); } else if (s.equals("%loinc")) { @@ -1713,7 +1713,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); + return hostServices.resolveConstant(this, context.appInfo, s.substring(1), beforeContext, explicitConstant); } } @@ -2532,7 +2532,7 @@ public class FHIRPathEngine { private List opMemberOf(ExecutionContext context, List left, List right, ExpressionNode expr) throws FHIRException { boolean ans = false; String url = right.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url); + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs != null) { for (Base l : left) { if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) { @@ -3031,7 +3031,7 @@ public class FHIRPathEngine { } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (constant instanceof BooleanType) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } else if (constant instanceof IntegerType) { @@ -3041,7 +3041,7 @@ public class FHIRPathEngine { } else if (constant instanceof Quantity) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } else if (constant instanceof FHIRConstant) { - return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr, explicitConstant); } else if (constant == null) { return new TypeDetails(CollectionStatus.SINGLETON); } else { @@ -3049,7 +3049,7 @@ public class FHIRPathEngine { } } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); @@ -3087,7 +3087,7 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { - return hostServices.resolveConstantType(context.appInfo, s); + return hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant); } } @@ -3095,7 +3095,7 @@ public class FHIRPathEngine { List result = new ArrayList(); if (atEntry && context.appInfo != null && hostServices != null) { // we'll see if the name matches a constant known by the context. - List temp = hostServices.resolveConstant(context.appInfo, exp.getName(), true); + List temp = hostServices.resolveConstant(this, context.appInfo, exp.getName(), true, false); if (!temp.isEmpty()) { result.addAll(temp); return result; @@ -3123,7 +3123,7 @@ public class FHIRPathEngine { if (atEntry && context.appInfo != null && hostServices != null && result.isEmpty()) { // well, we didn't get a match on the name - we'll see if the name matches a constant known by the context. // (if the name does match, and the user wants to get the constant value, they'll have to try harder... - result.addAll(hostServices.resolveConstant(context.appInfo, exp.getName(), false)); + result.addAll(hostServices.resolveConstant(this, context.appInfo, exp.getName(), false, false)); } return result; } @@ -3133,11 +3133,11 @@ public class FHIRPathEngine { } - private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr, boolean explicitConstant) throws PathEngineException, DefinitionException { if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); } - return hostServices.resolveConstantType(context.appInfo, name); + return hostServices.resolveConstantType(this, context.appInfo, name, explicitConstant); } private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry, TypeDetails focus, Set elementDependencies) throws PathEngineException, DefinitionException { @@ -3585,7 +3585,7 @@ public class FHIRPathEngine { } case Custom : { - return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes); + return hostServices.checkFunction(this, context.appInfo,exp.getName(), focus, paramTypes); } default: break; @@ -3818,7 +3818,7 @@ public class FHIRPathEngine { params.add(execute(context, focus, p, true)); } } - return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); + return hostServices.executeFunction(this, context.appInfo, focus, exp.getName(), params); } default: throw new Error("not Implemented yet"); @@ -4481,7 +4481,7 @@ public class FHIRPathEngine { } String url = nl.get(0).primitiveValue(); - ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url); + ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url); if (vs == null) { return new ArrayList(); } @@ -5108,7 +5108,7 @@ public class FHIRPathEngine { } } else if (hostServices != null) { try { - res = hostServices.resolveReference(context.appInfo, s, refContext); + res = hostServices.resolveReference(this, context.appInfo, s, refContext); } catch (Exception e) { res = null; } @@ -5616,7 +5616,7 @@ public class FHIRPathEngine { result.add(new BooleanType(false).noExtensions()); } else { String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); - result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); + result.add(new BooleanType(hostServices.conformsToProfile(this, context.appInfo, focus.get(0), url)).noExtensions()); } return result; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java index 32ba081b9..091491246 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java @@ -741,7 +741,7 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (ctxt.loopVars.containsKey(name)) return new ArrayList(Arrays.asList(ctxt.loopVars.get(name))); @@ -749,15 +749,15 @@ public class LiquidEngine implements IEvaluationContext { return new ArrayList(Arrays.asList(ctxt.globalVars.get(name))); if (externalHostServices == null) return new ArrayList(); - return externalHostServices.resolveConstant(ctxt.externalContext, name, beforeContext); + return externalHostServices.resolveConstant(engine, ctxt.externalContext, name, beforeContext, explicitConstant); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.resolveConstantType(ctxt.externalContext, name); + return externalHostServices.resolveConstantType(engine, ctxt.externalContext, name, explicitConstant); } @Override @@ -768,49 +768,49 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if (externalHostServices == null) return null; - return externalHostServices.resolveFunction(functionName); + return externalHostServices.resolveFunction(engine, functionName); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.checkFunction(ctxt.externalContext, functionName, parameters); + return externalHostServices.checkFunction(engine, ctxt.externalContext, functionName, focus, parameters); } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.executeFunction(ctxt.externalContext, focus, functionName, parameters); + return externalHostServices.executeFunction(engine, ctxt.externalContext, focus, functionName, parameters); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return resolveReference(ctxt.externalContext, url, refContext); + return resolveReference(engine, ctxt.externalContext, url, refContext); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (externalHostServices == null) return false; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return conformsToProfile(ctxt.externalContext, item, url); + return conformsToProfile(engine, ctxt.externalContext, item, url); } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { LiquidEngineContext ctxt = (LiquidEngineContext) appContext; if (externalHostServices != null) - return externalHostServices.resolveValueSet(ctxt.externalContext, url); + return externalHostServices.resolveValueSet(engine, ctxt.externalContext, url); else return engine.getWorker().fetchResource(ValueSet.class, url); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java index 4c293d895..1e1a7671f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java @@ -318,12 +318,12 @@ public class Runner implements IEvaluationContext { } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet: resolveConstant"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet: resolveConstantType"); } @@ -333,7 +333,7 @@ public class Runner implements IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { switch (functionName) { case "getResourceKey" : return new FunctionDetails("Unique Key for resource", 0, 0); case "getReferenceKey" : return new FunctionDetails("Unique Key for resource that is the target of the reference", 0, 1); @@ -341,7 +341,7 @@ public class Runner implements IEvaluationContext { } } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { switch (functionName) { case "getResourceKey" : return new TypeDetails(CollectionStatus.SINGLETON, "string"); case "getReferenceKey" : return new TypeDetails(CollectionStatus.SINGLETON, "string"); @@ -350,7 +350,7 @@ public class Runner implements IEvaluationContext { } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { switch (functionName) { case "getResourceKey" : return executeResourceKey(focus); case "getReferenceKey" : return executeReferenceKey(focus, parameters); @@ -420,17 +420,17 @@ public class Runner implements IEvaluationContext { return null; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { throw new Error("Not implemented yet: resolveReference"); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { throw new Error("Not implemented yet: conformsToProfile"); } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not implemented yet: resolveValueSet"); } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServices.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServices.java index 678d2b357..fce5c072c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServices.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServices.java @@ -24,7 +24,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { this.structureMapUtilities = structureMapUtilities; } - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { Variables vars = (Variables) appContext; Base res = vars.get(VariableMode.INPUT, name); if (res == null) @@ -36,7 +36,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (!(appContext instanceof VariablesForProfiling)) throw new Error("Internal Logic Error (wrong type '" + appContext.getClass().getName() + "' in resolveConstantType)"); VariablesForProfiling vars = (VariablesForProfiling) appContext; @@ -52,22 +52,22 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; // throw new Error("Not Implemented Yet"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new Error("Not Implemented Yet"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { if (structureMapUtilities.getServices() == null) return null; return structureMapUtilities.getServices().resolveReference(appContext, url); @@ -81,7 +81,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = structureMapUtilities.getWorker().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -96,7 +96,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return structureMapUtilities.getWorker().fetchResource(ValueSet.class, url); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java index b82d84db9..88de6464e 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java @@ -46,12 +46,12 @@ public class FHIRPathTests { public class FHIRPathTestEvaluationServices implements IEvaluationContext { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstant), when item is element"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstantType), when item is element"); } @@ -61,27 +61,27 @@ public class FHIRPathTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveFunction), when item is element (for " + functionName + ")"); } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.checkFunction), when item is element"); } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element"); } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveReference), when item is element"); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { if (url.equals("http://hl7.org/fhir/StructureDefinition/Patient")) return true; if (url.equals("http://hl7.org/fhir/StructureDefinition/Person")) @@ -91,7 +91,7 @@ public class FHIRPathTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return TestingUtilities.getSharedWorkerContext().fetchResource(ValueSet.class, url); } @@ -291,7 +291,7 @@ public class FHIRPathTests { final String DUMMY_CONSTANT_2 = "dummyConstant2"; fp.setHostServices(new FHIRPathTestEvaluationServices() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Arrays.asList( new StringType(DUMMY_CONSTANT_1).noExtensions(), diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index d089a373d..4288d2822 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -337,12 +337,12 @@ public class SnapShotGenerationTests { // FHIRPath methods @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @@ -353,21 +353,21 @@ public class SnapShotGenerationTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if ("fixture".equals(functionName)) return new FunctionDetails("Access a fixture defined in the testing context", 0, 1); return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if ("fixture".equals(functionName)) return new TypeDetails(CollectionStatus.SINGLETON, TestingUtilities.getSharedWorkerContext().getResourceNamesAsSet()); return null; } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); Resource res = fetchFixture(id); @@ -382,13 +382,13 @@ public class SnapShotGenerationTests { } @Override - public Base resolveReference(Object appContext, String url, Base refContext) { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) { // TODO Auto-generated method stub return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = TestingUtilities.getSharedWorkerContext().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -416,7 +416,7 @@ public class SnapShotGenerationTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not implemented yet"); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServicesTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServicesTest.java index d0766fb4c..5bb2b8a80 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServicesTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/structuremap/FHIRPathHostServicesTest.java @@ -24,7 +24,7 @@ class FHIRPathHostServicesTest { public void testrResolveValueSet() throws IOException, FHIRException { StructureMapUtilities scu = new StructureMapUtilities(context); FHIRPathHostServices fphs = new FHIRPathHostServices(scu); - ValueSet v = fphs.resolveValueSet(null, "http://hl7.org/fhir/ValueSet/FHIR-version"); + ValueSet v = fphs.resolveValueSet(null, null, "http://hl7.org/fhir/ValueSet/FHIR-version"); Assertions.assertNotNull(v); Assertions.assertEquals("http://hl7.org/fhir/ValueSet/FHIR-version", v.getUrl()); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index 8ba86322d..e8aff5e20 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -316,12 +316,12 @@ public class SnapShotGenerationXTests { // FHIRPath methods @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { throw new Error("Not implemented yet"); } @@ -332,21 +332,21 @@ public class SnapShotGenerationXTests { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { if ("fixture".equals(functionName)) return new FunctionDetails("Access a fixture defined in the testing context", 0, 1); return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { if ("fixture".equals(functionName)) return new TypeDetails(CollectionStatus.SINGLETON, UtilitiesXTests.context(version).getResourceNamesAsSet()); return null; } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); Resource res = fetchFixture(id); @@ -361,13 +361,13 @@ public class SnapShotGenerationXTests { } @Override - public Base resolveReference(Object appContext, String url, Base refContext) { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) { // TODO Auto-generated method stub return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = UtilitiesXTests.context(version).newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -393,7 +393,7 @@ public class SnapShotGenerationXTests { } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { throw new Error("Not implemented yet"); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index d7429594c..55e5ec2fb 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -656,12 +656,12 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return new ArrayList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { return null; } @@ -671,22 +671,22 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { return null; } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) { if (url.equals("Patient/test")) return new Patient(); return null; @@ -752,7 +752,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { IResourceValidator val = vCurr.getContext().newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { @@ -766,7 +766,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return vCurr.getContext().fetchResource(ValueSet.class, url); } From f3ddf1a0f416fde4f2cc187b02f389fefd4e13d9 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 23 Oct 2023 10:25:03 +1100 Subject: [PATCH 2/8] Implement slice() function in validator --- .../org/hl7/fhir/r5/elementmodel/Element.java | 46 ++++++++ .../instance/InstanceValidator.java | 103 +++++++++++++++--- 2 files changed, 131 insertions(+), 18 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index 546dfd9a6..373d4e32e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java @@ -38,6 +38,7 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.context.ContextUtilities; +import org.hl7.fhir.r5.elementmodel.Element.SliceDefinition; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.extensions.ExtensionsUtils; import org.hl7.fhir.r5.model.Base; @@ -73,6 +74,32 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode; * */ public class Element extends Base implements NamedItem { + public class SliceDefinition { + + private StructureDefinition profile; + private ElementDefinition definition; + private ElementDefinition slice; + + public SliceDefinition(StructureDefinition profile, ElementDefinition definition, ElementDefinition slice) { + this.profile = profile; + this.definition = definition; + this.slice = slice; + } + + public StructureDefinition getProfile() { + return profile; + } + + public ElementDefinition getDefinition() { + return definition; + } + + public ElementDefinition getSlice() { + return slice; + } + + } + private static final HashSet extensionList = new HashSet<>(Arrays.asList("extension", "modifierExtension")); public enum SpecialElement { @@ -133,6 +160,7 @@ public class Element extends Base implements NamedItem { private boolean ignorePropertyOrder; private FhirFormat format; private Object nativeObject; + private List sliceDefinitions; public Element(String name) { super(); @@ -1498,5 +1526,23 @@ public class Element extends Base implements NamedItem { children.removeAll(rem); } + public void addSliceDefinition(StructureDefinition profile, ElementDefinition definition, ElementDefinition slice) { + if (sliceDefinitions == null) { + sliceDefinitions = new ArrayList<>(); + } + sliceDefinitions.add(new SliceDefinition(profile, definition, slice)); + } + + public boolean hasSlice(StructureDefinition sd, String sliceName) { + if (sliceDefinitions != null) { + for (SliceDefinition def : sliceDefinitions) { + if (def.profile == sd && sliceName.equals(def.definition.getSliceName())) { + return true; + } + } + } + return false; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index fbd90c707..35c4d63e0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -275,16 +275,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private class ValidatorHostServices implements IEvaluationContext { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { ValidationContext c = (ValidationContext) appContext; + if ("profile".equals(name) && explicitConstant) { + List b = new ArrayList<>(); + if (c.getProfile() != null) { + b.add(c.getProfile()); + } + return b; + } if (externalHostServices != null) - return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); + return externalHostServices.resolveConstant(engine, c.getAppContext(), name, beforeContext, explicitConstant); else return new ArrayList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException { if (appContext instanceof VariableSet) { VariableSet vars = (VariableSet) appContext; VariableDefn v = vars.getVariable(name.substring(1)); @@ -296,7 +303,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } ValidationContext c = (ValidationContext) appContext; if (externalHostServices != null) - return externalHostServices.resolveConstantType(c.getAppContext(), name); + return externalHostServices.resolveConstantType(engine, c.getAppContext(), name, explicitConstant); else return null; } @@ -310,22 +317,76 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } @Override - public FunctionDetails resolveFunction(String functionName) { - throw new FHIRException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_, functionName)); + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { + switch (functionName) { + case "slice": return new FunctionDetails("Returns the given slice as defined in the given structure definition. If in an invariant, First parameter can be %profile - current profile", 2, 2); + default: throw new FHIRException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_, functionName)); + } } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION)); + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException { + + switch (functionName) { + case "slice": + // todo: check parameters + return focus; + + default: throw new Error(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION)); + } } @Override - public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { - throw new Error(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION)); + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) { + switch (functionName) { + case "slice": return executeSlice(engine, appContext, focus, parameters); + default: throw new Error(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION)); + } + } + + private List executeSlice(FHIRPathEngine engine, Object appContext, List focus, List> parameters) { + ValidationContext c = (ValidationContext) appContext; + + List res = new ArrayList<>(); + if (parameters.size() != 2 && !(appContext instanceof ValidationContext)) { + return res; + } + + StructureDefinition sd = null; + // if present, first parameter must be a singleton that points to the current profile + if (parameters.get(0).size() > 1) { + return res; + } else if (parameters.get(0).size() == 1) { // if it's there, we have to check it + Base b = parameters.get(0).get(0); + if (b.isPrimitive()) { + sd = context.fetchResource(StructureDefinition.class, b.primitiveValue()); + } else if (b instanceof StructureDefinition) { + sd = (StructureDefinition) b; + } + } else { + sd = c.getProfile(); + } + + // second parameter must be present + if (parameters.get(1).size() != 1) { + return res; + } + String name = parameters.get(1).get(0).primitiveValue(); + if (!Utilities.noString(name)) { + for (Base b : focus) { + if (b instanceof Element) { + Element e = (Element) b; + if (e.hasSlice(sd, name)) { + res.add(e); + } + } + } + } + return res; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException { ValidationContext c = (ValidationContext) appContext; if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { @@ -356,7 +417,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (externalHostServices != null) { - return setParentsBase(externalHostServices.resolveReference(c.getAppContext(), url, refContext)); + return setParentsBase(externalHostServices.resolveReference(engine, c.getAppContext(), url, refContext)); } else if (fetcher != null) { try { return setParents(fetcher.fetch(InstanceValidator.this, c.getAppContext(), url)); @@ -370,7 +431,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException { ValidationContext ctxt = (ValidationContext) appContext; StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); if (sd == null) { @@ -414,7 +475,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { ValidationContext c = (ValidationContext) appContext; if (c.getProfile() != null && url.startsWith("#")) { for (Resource r : c.getProfile().getContained()) { @@ -5760,8 +5821,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } ValidationInfo vi = element.addDefinition(profile, definition, mode); - // check type invariants - ok = checkInvariants(valContext, errors, profile, definition, resource, element, stack, false) & ok; if (definition.getFixed() != null) { ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getVersionedUrl(), definition.getSliceName(), null, false) && ok; } @@ -5792,6 +5851,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat BooleanHolder bh = new BooleanHolder(); List problematicPaths = assignChildren(valContext, errors, profile, resource, stack, childDefinitions, children, bh); ok = bh.ok() && ok; + for (ElementInfo ei : children) { + ei.getElement().addSliceDefinition(profile, ei.getDefinition(), ei.getSlice()); + } ok = checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths) && ok; // 4. check order if any slices are ordered. (todo) @@ -5800,8 +5862,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (ElementInfo ei : children) { ok = checkChild(valContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, pct, mode) && ok; } - vi.setValid(ok); + + // check type invariants (after we've sliced the children) + ok = checkInvariants(valContext, errors, profile, definition, resource, element, stack, false) & ok; + vi.setValid(ok); if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_PROFILE_STYLE) && "cda".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_PROFILE_STYLE))) { List templates = element.getChildren("templateId"); @@ -6694,7 +6759,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.fpe(t); inv.setUserData("validator.expression.cache", n); } - + + valContext.setProfile(profile); + boolean invOK; String msg; try { From be9f5e0d3660d1ad1fcf66b6d1893cde6d58d999 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 24 Oct 2023 17:36:23 +1100 Subject: [PATCH 3/8] more improvements to profile generation --- .../fhir/r5/context/BaseWorkerContext.java | 18 +- .../hl7/fhir/r5/profilemodel/PEBuilder.java | 2 +- .../fhir/r5/profilemodel/PEDefinition.java | 10 +- .../r5/profilemodel/gen/BindingStrength.java | 7 + .../fhir/r5/profilemodel/gen/Definition.java | 7 + .../hl7/fhir/r5/profilemodel/gen/Doco.java | 7 + .../hl7/fhir/r5/profilemodel/gen/Label.java | 7 + .../org/hl7/fhir/r5/profilemodel/gen/Max.java | 7 + .../org/hl7/fhir/r5/profilemodel/gen/Min.java | 7 + .../fhir/r5/profilemodel/gen/MustSupport.java | 7 + .../r5/profilemodel/gen/PECodeGenerator.java | 264 ++++++++++++++++-- .../fhir/r5/profilemodel/gen/ValueSet.java | 7 + .../r5/terminologies/CodeSystemUtilities.java | 2 +- .../terminologies/TerminologyUtilities.java | 27 ++ .../r5/terminologies/ValueSetUtilities.java | 19 +- .../org/hl7/fhir/r5/profiles/PETests.java | 4 +- .../r5/profiles/TestComplexExtension.java | 47 ++-- .../fhir/r5/profiles/TestDatatypeProfile.java | 34 ++- .../org/hl7/fhir/r5/profiles/TestProfile.java | 166 ++++++++++- .../org/hl7/fhir/utilities/Utilities.java | 25 ++ 20 files changed, 590 insertions(+), 84 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/BindingStrength.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Definition.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Doco.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Label.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Max.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Min.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/MustSupport.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ValueSet.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index d83f572ea..7908ebe1b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -881,7 +881,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte if (pIn == null) { throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS)); } - if (vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-time-units") || vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-distance-units")) { + if (vs.hasUrl() && (vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-time-units") || vs.getUrl().equals("http://hl7.org/fhir/ValueSet/all-distance-units"))) { return new ValueSetExpansionOutcome("This value set is not expanded correctly at this time (will be fixed in a future version)", TerminologyServiceErrorClass.VALUESET_UNSUPPORTED, false); } @@ -1792,6 +1792,22 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte if (uri == null) { return null; } + if (uri.startsWith("#")) { + if (sourceForReference != null && sourceForReference instanceof DomainResource) { + for (Resource r : ((DomainResource) sourceForReference).getContained()) { + if (r.getClass() == class_ &&( "#"+r.getIdBase()).equals(uri)) { + if (r instanceof CanonicalResource) { + CanonicalResource cr = (CanonicalResource) r; + if (!cr.hasUrl()) { + cr.setUrl(Utilities.makeUuidUrn()); + } + } + return (T) r; + } + } + } + return null; + } if (QA_CHECK_REFERENCE_SOURCE) { // it can be tricky to trace the source of a reference correctly. The code isn't water tight, diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java index 2063a9045..73eddbfc5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java @@ -531,7 +531,7 @@ public class PEBuilder { protected void populateByProfile(Base base, PEDefinition definition) { if (definition.types().size() == 1) { for (PEDefinition pe : definition.directChildren(true)) { - if (pe.fixedValue()) { + if (pe.hasFixedValue()) { if (pe.definition().hasPattern()) { base.setProperty(pe.schemaName(), pe.definition().getPattern()); } else { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java index f185e127b..f5dbe05ba 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java @@ -37,6 +37,7 @@ import org.apache.xmlbeans.impl.xb.xsdschema.All; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition; @@ -236,10 +237,15 @@ public abstract class PEDefinition { /** * @return True if the element has a fixed value. This will always be false if fixedProps = false when the builder is created */ - public boolean fixedValue() { + public boolean hasFixedValue() { return definition.hasFixed() || definition.hasPattern(); } + + public DataType getFixedValue() { + return definition.hasFixed() ? definition.getFixed() : definition.getPattern(); + } + protected abstract void makeChildren(String typeUrl, List children, boolean allFixed); @Override @@ -290,7 +296,7 @@ public abstract class PEDefinition { public boolean isList() { - return "*".equals(definition.getMax()); + return "*".equals(definition.getMax()) || (Utilities.parseInt(definition.getMax(), 2) > 1); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/BindingStrength.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/BindingStrength.java new file mode 100644 index 000000000..6f7d369c9 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/BindingStrength.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface BindingStrength { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Definition.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Definition.java new file mode 100644 index 000000000..d0febdf88 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Definition.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface Definition { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Doco.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Doco.java new file mode 100644 index 000000000..3e30eca7b --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Doco.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface Doco { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Label.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Label.java new file mode 100644 index 000000000..0b8fa2d6f --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Label.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface Label { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Max.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Max.java new file mode 100644 index 000000000..94425bcb2 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Max.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface Max { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Min.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Min.java new file mode 100644 index 000000000..0bfc78835 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/Min.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface Min { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/MustSupport.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/MustSupport.java new file mode 100644 index 000000000..02284f8bd --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/MustSupport.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface MustSupport { + + boolean value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java index 8c0e2f224..e8e4d50b7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java @@ -33,22 +33,30 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.TimeZone; import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.DataType; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.Identifier; import org.hl7.fhir.r5.model.Observation; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; import org.hl7.fhir.r5.profilemodel.gen.PECodeGenerator.ExtensionPolicy; +import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.profilemodel.PEDefinition; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEType; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -73,7 +81,12 @@ public class PECodeGenerator { private String doco; private String url; private boolean isResource; + private Set unfixed = new HashSet<>(); + private Set enumNames = new HashSet<>(); + + private StringBuilder inits = new StringBuilder(); private StringBuilder fields = new StringBuilder(); + private StringBuilder enums = new StringBuilder(); private StringBuilder load = new StringBuilder(); private StringBuilder save = new StringBuilder(); private StringBuilder clear = new StringBuilder(); @@ -82,10 +95,10 @@ public class PECodeGenerator { private StringBuilder hash = new StringBuilder(); public void genId() { if (isResource) { - genField(true, "id", "String", "id", "", false, ""); - genAccessors(true, false, "id", "String", "", "String", "String", "Id", "Ids", false, "", false); - genLoad(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, null); - genSave(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, false, null); + genField(true, "id", "String", "id", "", false, "", 0, 1, null); + genAccessors(true, false, "id", "String", "", "String", "String", "Id", "Ids", false, "", false, false); + genLoad(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, null, false); + genSave(true, false, "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, false, null, false); genClear(false, "id"); } } @@ -106,15 +119,27 @@ public class PECodeGenerator { w(b, " private static final String CANONICAL_URL = \""+url+"\";"); w(b); } + if (enums.length() > 0) { + w(b, enums.toString()); + } w(b, fields.toString()); - jdoc(b, "Parameter-less constructor. If you use this, the fixed values won't be filled out - they'll be missing. They'll be filled in if/when you call build, so they won't be missing from the resource, only from this particular object model", 2, true); + if (unfixed.isEmpty()) { + jdoc(b, "Parameter-less constructor.", 2, true); + } else { + jdoc(b, "Parameter-less constructor. If you use this, the fixed values on "+CommaSeparatedStringBuilder.join(",", unfixed)+" won't be filled out - they'll be missing. They'll be filled in if/when you call build, so they won't be missing from the resource, only from this particular object model", 2, true); + } w(b, " public "+name+"() {"); - w(b, " // todo"); + if (inits.length() > 0) { + w(b, " initFixedValues();"); + } w(b, " }"); w(b); if (isResource) { jdoc(b, "Construct an instance of the object, and fill out all the fixed values ", 2, true); w(b, " public "+name+"(IWorkerContext context) {"); + if (inits.length() > 0) { + w(b, " initFixedValues();"); + } w(b, " workerContext = context;"); w(b, " PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true);"); w(b, " PEInstance src = builder.buildPEInstance(CANONICAL_URL, builder.createResource(CANONICAL_URL, false));"); @@ -171,6 +196,12 @@ public class PECodeGenerator { w(b, save.toString()); w(b, " }"); w(b); + if (inits.length() > 0) { + w(b, " private void initFixedValues() {"); + w(b, inits.toString()); + w(b, " }"); + w(b); + } w(b, accessors.toString()); w(b); w(b, " public void clear() {"); @@ -180,10 +211,114 @@ public class PECodeGenerator { w(b, "}"); } + private String generateEnum(PEDefinition source, PEDefinition field) { + if (field.definition().hasBinding() && !field.hasFixedValue()) { + ElementDefinitionBindingComponent binding = field.definition().getBinding(); + if (binding.getStrength() == org.hl7.fhir.r5.model.Enumerations.BindingStrength.REQUIRED && binding.hasValueSet()) { + org.hl7.fhir.r5.model.ValueSet vs = workerContext.fetchResource(org.hl7.fhir.r5.model.ValueSet.class, binding.getValueSet(), field.getProfile()); + if (vs != null) { + ValueSetExpansionOutcome vse = workerContext.expandVS(vs, false, false); + if (vse.isOk()) { + String baseName = Utilities.nmtokenize(Utilities.singularise(vs.getName())); + String name = baseName; + int c = 0; + while (enumNames.contains(name)) { + c++; + name = baseName+c; + } + w(enums, " public enum "+name+" {"); + for (int i = 0; i < vse.getValueset().getExpansion().getContains().size(); i++) { + ValueSetExpansionContainsComponent cc = vse.getValueset().getExpansion().getContains().get(i); + String code = Utilities.nmtokenize(cc.getCode()).toUpperCase(); + if (cc.getAbstract()) { + code = "_"+code; + } + cc.setUserData("java.code", code); + w(enums, " "+code+(i < vse.getValueset().getExpansion().getContains().size() - 1 ? "," : ";")+" // \""+cc.getDisplay()+"\" = "+cc.getSystem()+"#"+cc.getCode()); + } + w(enums, ""); + w(enums, " public static "+name+" fromCode(String s) {"); + w(enums, " switch (s) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + w(enums, " case \""+cc.getCode()+"\": return "+cc.getUserString("java.code")+";"); + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); + w(enums, " public static "+name+" fromCoding(Coding c) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + if (cc.hasVersion()) { + w(enums, " if (\""+cc.getSystem()+"\".equals(c.getSystem()) && \""+cc.getCode()+"\".equals(c.getCode()) && (!c.hasVersion() || \""+cc.getVersion()+"\".equals(c.getVersion()))) {"); + } else { + w(enums, " if (\""+cc.getSystem()+"\".equals(c.getSystem()) && \""+cc.getCode()+"\".equals(c.getCode())) {"); + } + w(enums, " return "+cc.getUserString("java.code")+";"); + w(enums, " }"); + } + w(enums, " return null;"); + w(enums, " }"); + w(enums, ""); + w(enums, " public static "+name+" fromCodeableConcept(CodeableConcept cc) {"); + w(enums, " for (Coding c : cc.getCoding()) {"); + w(enums, " "+name+" v = fromCoding(c);"); + w(enums, " if (v != null) {"); + w(enums, " return v;"); + w(enums, " }"); + w(enums, " }"); + w(enums, " return null;"); + w(enums, " }"); + w(enums, ""); + + w(enums, " public String toDisplay() {"); + w(enums, " switch (this) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + w(enums, " case "+cc.getUserString("java.code")+": return \""+Utilities.escapeJava(cc.getDisplay())+"\";"); + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); + + w(enums, " public String toCode() {"); + w(enums, " switch (this) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + w(enums, " case "+cc.getUserString("java.code")+": return \""+cc.getCode()+"\";"); + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); + w(enums, " public Coding toCoding() {"); + w(enums, " switch (this) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + if (cc.hasVersion()) { + w(enums, " case "+cc.getUserString("java.code")+": return new Coding().setSystem(\""+cc.getSystem()+"\").setVersion(\""+cc.getVersion()+"\").setCode()\""+cc.getCode()+"\";"); + } else { + w(enums, " case "+cc.getUserString("java.code")+": return new Coding().setSystem(\""+cc.getSystem()+"\").setCode(\""+cc.getCode()+"\");"); + } + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); + w(enums, " public CodeableConcept toCodeableConcept() {"); + w(enums, " Coding c = toCoding();"); + w(enums, " return c == null ? null : new CodeableConcept().addCoding(c);"); + w(enums, " }"); + w(enums, " }"); + return name; + } + } + } + } + return null; + } private void defineField(PEDefinition source, PEDefinition field) { if (field.types().size() == 1) { StructureDefinition sd = workerContext.fetchTypeDefinition(field.types().get(0).getUrl()); if (sd != null) { + String enumName = generateEnum(source, field); boolean isPrim = sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; boolean isAbstract = sd.getAbstract(); String name = field.name().replace("[x]", ""); @@ -191,7 +326,12 @@ public class PECodeGenerator { String type = null; String init = ""; String ptype = type; - if (isPrim) { + boolean isEnum = false; + if (enumName != null) { + type = enumName; + ptype = enumName; + isEnum = true; + } else if (isPrim) { // todo: are we extension-less? type = Utilities.capitalize(field.types().get(0).getName()+"Type"); ptype = getPrimitiveType(sd); @@ -210,18 +350,20 @@ public class PECodeGenerator { String csname = Utilities.capitalize(sname); String nn = field.min() == 1 ? "// @NotNull" : ""; boolean isExtension = field.isExtension(); - genField(isPrim, name, ptype, ltype, nn, field.isList(), field.shortDocumentation()); - genAccessors(isPrim, isAbstract, name, type, init, ptype, ltype, cname, csname, field.isList(), field.documentation(), field.fixedValue()); - genLoad(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.fixedValue(), field.types().get(0)); - genSave(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.fixedValue(), isExtension, field.types().get(0)); + genField(isPrim, name, ptype, ltype, nn, field.isList(), field.shortDocumentation(), field.min(), field.max(), field.definition()); + if (isPrim && field.hasFixedValue()) { + genFixed(name, ptype, field.getFixedValue()); + } + genAccessors(isPrim, isAbstract, name, type, init, ptype, ltype, cname, csname, field.isList(), field.documentation(), field.hasFixedValue(), isEnum); + genLoad(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.hasFixedValue(), field.types().get(0), isEnum); + genSave(isPrim, isAbstract, name, sname, type, init, ptype, ltype, cname, csname, field.isList(), field.hasFixedValue(), isExtension, field.types().get(0), isEnum); genClear(field.isList(), name); } } else { // ignoring polymorphics for now } - } - + private void genClear(boolean list, String name) { if (list) { w(clear, " "+name+".clear();"); @@ -230,11 +372,21 @@ public class PECodeGenerator { } } - private void genLoad(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, PEType typeInfo) { + private void genLoad(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, PEType typeInfo, boolean isEnum) { if (isList) { w(load, " for (PEInstance item : src.children(\""+sname+"\")) {"); w(load, " "+name+".add(("+type+") item.asDataType());"); w(load, " }"); + } else if (isEnum) { + w(load, " if (src.hasChild(\""+name+"\")) {"); + if ("CodeableConcept".equals(typeInfo.getName())) { + w(load, " "+name+" = "+type+".fromCodeableConcept((CodeableConcept) src.child(\""+name+"\").asDataType());"); + } else if ("Coding".equals(typeInfo.getName())) { + w(load, " "+name+" = "+type+".fromCoding((Coding) src.child(\""+name+"\").asDataType());"); + } else { + w(load, " "+name+" = "+type+".fromCode(src.child(\""+name+"\").asDataType()).primitiveValue());"); + } + w(load, " }"); } else if (isPrim) { w(load, " if (src.hasChild(\""+name+"\")) {"); if ("CodeType".equals(type)) { @@ -255,7 +407,7 @@ public class PECodeGenerator { } } - private void genSave(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, boolean isExtension, PEType typeInfo) { + private void genSave(boolean isPrim, boolean isAbstract, String name, String sname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, boolean isExtension, PEType typeInfo, boolean isEnum) { w(save, " tgt.clear(\""+sname+"\");"); if (isList) { w(save, " for ("+type+" item : "+name+") {"); @@ -265,6 +417,16 @@ public class PECodeGenerator { w(save, " tgt.addChild(\""+sname+"\", item);"); } w(save, " }"); + } else if (isEnum) { + w(save, " if ("+name+" != null) {"); + if ("CodeableConcept".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+sname+"\", "+name+".toCodeableConcept());"); + } else if ("Coding".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+sname+"\", "+name+".toCoding());"); + } else { + w(save, " tgt.addChild(\""+sname+"\", "+name+").toCode();"); + } + w(save, " }"); } else if (isPrim) { w(save, " if ("+name+" != null) {"); if (isExtension) { @@ -290,21 +452,27 @@ public class PECodeGenerator { } } - private void genAccessors(boolean isPrim, boolean isAbstract, String name, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, String shortDoco, boolean isFixed) { + private void genAccessors(boolean isPrim, boolean isAbstract, String name, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, String shortDoco, boolean isFixed, boolean isEnum) { jdoc(accessors, doco, 2, true); - if (isPrim && extensionPolicy != ExtensionPolicy.Primitives && !isList) { + if ((isEnum || isPrim) && extensionPolicy != ExtensionPolicy.Primitives && !isList) { w(accessors, " public "+ptype+" get"+cname+"() {"); w(accessors, " return "+name+";"); w(accessors, " }"); w(accessors); - w(accessors, " public "+this.name+" set"+cname+"("+ptype+" value) {"); - w(accessors, " this."+name+" = value;"); - w(accessors, " return this;"); - w(accessors, " }"); - w(accessors); - w(accessors, " public boolean has"+cname+"() {"); - w(accessors, " return "+name+" != null;"); - w(accessors, " }"); + if (isFixed) { + w(accessors, " public boolean has"+cname+"() {"); + w(accessors, " return true;"); + w(accessors, " }"); + } else { + w(accessors, " public "+this.name+" set"+cname+"("+ptype+" value) {"); + w(accessors, " this."+name+" = value;"); + w(accessors, " return this;"); + w(accessors, " }"); + w(accessors); + w(accessors, " public boolean has"+cname+"() {"); + w(accessors, " return "+name+" != null;"); + w(accessors, " }"); + } } else { if (isPrim && !isList) { w(accessors, " public "+ptype+" get"+cname+"() {"); @@ -379,8 +547,27 @@ public class PECodeGenerator { w(accessors); } - private void genField(boolean isPrim, String name, String ptype, String ltype, String nn, boolean isList, String shortDoco) { - // jdoc(fields, field.documentation(), 2, true); + private void genField(boolean isPrim, String name, String ptype, String ltype, String nn, boolean isList, String shortDoco, int min, int max, ElementDefinition ed) { +// jdoc(fields, shortDoco, 2, true); + w(fields, " @Min(\""+min+"\") @Max(\""+(max == Integer.MAX_VALUE ? "*" : max) +"\")"+(" @Doco(\""+Utilities.escapeJava(shortDoco)+"\")")); + if (ed != null) { + if (ed.hasBinding() && ed.getBinding().hasValueSet()) { + w(fields, " @BindingStrength(\""+ed.getBinding().getStrength().toCode()+"\") @ValueSet(\""+ed.getBinding().getValueSet()+"\")"); + } + if (ed.getMustSupport()) { + w(fields, " @MustSupport(true)"); + } + if (ed.hasLabel() || ed.hasDefinition()) { + String s = ""; + if (ed.hasLabel()) { + s = s + " @Label(\""+Utilities.escapeJava(ed.getLabel())+"\")"; + } + if (ed.hasDefinition()) { + s = s + " @Definition(\""+Utilities.escapeJava(ed.getDefinition())+"\")"; + } + w(fields, " "+s); + } + } if (isPrim && extensionPolicy != ExtensionPolicy.Primitives && !isList) { w(fields, " private "+ptype+" "+name+";"+nn+" // "+shortDoco); } else if (isList) { @@ -388,6 +575,17 @@ public class PECodeGenerator { } else { w(fields, " private "+ltype+" "+name+";"+nn+" // "+shortDoco); } + w(fields, ""); + } + + + private void genFixed(String name, String pType, DataType fixedValue) { + if ("String".equals(pType)) { + w(inits, " "+name+" = \""+Utilities.escapeJava(fixedValue.primitiveValue())+"\";"); + } else { + unfixed.add(name); + System.out.println("Unable to handle the fixed value for "+name+" of type "+pType+" = "+fixedValue.toString()); + } } } @@ -501,6 +699,8 @@ public class PECodeGenerator { * */ public void execute() throws IOException { + imports = new StringBuilder(); + PEDefinition source = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true).buildPEDefinition(canonical); w(imports, "import java.util.List;"); w(imports, "import java.util.ArrayList;"); @@ -513,6 +713,16 @@ public class PECodeGenerator { w(imports, "import org.hl7.fhir.r5.profilemodel.PEInstance;"); w(imports, "import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;"); w(imports, "import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.Min;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.Max;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.Label;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.Doco;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.BindingStrength;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.ValueSet;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.MustSupport;"); + w(imports, "import org.hl7.fhir.r5.profilemodel.gen.Definition;"); + + PEGenClass cls = genClass(source); StringBuilder b = new StringBuilder(); w(b, "package "+pkgName+";"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ValueSet.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ValueSet.java new file mode 100644 index 000000000..5d54f3b79 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ValueSet.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.profilemodel.gen; + +public @interface ValueSet { + + String value(); + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java index a3a251414..aa5074afc 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java @@ -70,7 +70,7 @@ import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.Utilities; -public class CodeSystemUtilities { +public class CodeSystemUtilities extends TerminologyUtilities { public static class SystemReference { private String link; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java new file mode 100644 index 000000000..6dc1a7c9d --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java @@ -0,0 +1,27 @@ +package org.hl7.fhir.r5.terminologies; + +import java.util.HashSet; +import java.util.Set; + +import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.Identifier; +import org.hl7.fhir.utilities.json.model.JsonObject; + +public class TerminologyUtilities { + + public static Set listOids(CanonicalResource cr) { + Set oids = new HashSet<>(); + + if (cr.hasUrl() && cr.getUrl().startsWith("urn:oid:")) { + oids.add(cr.getUrl().substring(8)); + } + + for (Identifier id : cr.getIdentifier()) { + String v = id.getValue(); + if (v != null && v.startsWith("urn:oid:")) { + oids.add(v.substring(8)); + } + } + return oids; + } +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java index 7edcff541..87511c494 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java @@ -3,7 +3,9 @@ package org.hl7.fhir.r5.terminologies; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; /* Copyright (c) 2011+, HL7, Inc. @@ -66,7 +68,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.Utilities; -public class ValueSetUtilities { +public class ValueSetUtilities extends TerminologyUtilities { public static boolean isServerSide(String url) { @@ -401,5 +403,20 @@ public class ValueSetUtilities { return i; } + public static Set listSystems(IWorkerContext ctxt, ValueSet vs) { + Set systems = new HashSet<>(); + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + for (CanonicalType ct : inc.getValueSet()) { + ValueSet vsr = ctxt.fetchResource(ValueSet.class, ct.asStringValue(), vs); + if (vsr != null) { + systems.addAll(listSystems(ctxt, vsr)); + } + } + if (inc.hasSystem()) { + systems.add(inc.getSystem()); + } + } + return systems; + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java index 61a9cff73..0812934fc 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java @@ -112,7 +112,7 @@ public class PETests { checkElement(children.get(7), "extension", "complex", 0, 1, false, "http://hl7.org/fhir/test/StructureDefinition/pe-extension-complex", 4, "extension('http://hl7.org/fhir/test/StructureDefinition/pe-extension-complex')"); checkElement(children.get(8), "identifier", "identifier", 0, 1, false, "http://hl7.org/fhir/StructureDefinition/Identifier", 7, "identifier"); checkElement(children.get(9), "status", "status", 1, 1, true, "http://hl7.org/fhir/StructureDefinition/code", 2, "status"); - checkElement(children.get(10), "category", "category", 0, Integer.MAX_VALUE, false, "http://hl7.org/fhir/StructureDefinition/CodeableConcept", 3, "category"); + checkElement(children.get(10), "category", "category", 1, 1, false, "http://hl7.org/fhir/StructureDefinition/CodeableConcept", 3, "category"); checkElement(children.get(11), "code", "code", 1, 1, true, "http://hl7.org/fhir/StructureDefinition/CodeableConcept", 3, "code"); checkElement(children.get(12), "subject", "subject", 1, 1, false, "http://hl7.org/fhir/StructureDefinition/Reference", 5, "subject"); checkElement(children.get(13), "encounter", "encounter", 0, 1, false, "http://hl7.org/fhir/StructureDefinition/Reference", 5, "encounter"); @@ -154,7 +154,7 @@ public class PETests { Assertions.assertEquals(schemaName, pe.schemaName()); Assertions.assertEquals(min, pe.min()); Assertions.assertEquals(max, pe.max()); - Assertions.assertEquals(fixed, pe.fixedValue() || pe.isInFixedValue()); + Assertions.assertEquals(fixed, pe.hasFixedValue() || pe.isInFixedValue()); if (type != null) { Assertions.assertEquals(1, pe.types().size()); Assertions.assertEquals(type, pe.types().get(0).getUrl()); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestComplexExtension.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestComplexExtension.java index 066eb76c6..0786a662b 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestComplexExtension.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestComplexExtension.java @@ -12,33 +12,17 @@ import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; -import java.util.List; -import java.util.ArrayList; -import javax.annotation.Nullable; -import java.util.Date; +import org.hl7.fhir.r5.profilemodel.gen.Min; +import org.hl7.fhir.r5.profilemodel.gen.Max; +import org.hl7.fhir.r5.profilemodel.gen.Label; +import org.hl7.fhir.r5.profilemodel.gen.Doco; +import org.hl7.fhir.r5.profilemodel.gen.BindingStrength; +import org.hl7.fhir.r5.profilemodel.gen.ValueSet; +import org.hl7.fhir.r5.profilemodel.gen.MustSupport; +import org.hl7.fhir.r5.profilemodel.gen.Definition; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.*; -import org.hl7.fhir.r5.profilemodel.PEBuilder; -import org.hl7.fhir.r5.profilemodel.PEInstance; -import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; -import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; -import java.util.List; -import java.util.ArrayList; -import javax.annotation.Nullable; -import java.util.Date; - - -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.*; -import org.hl7.fhir.r5.profilemodel.PEBuilder; -import org.hl7.fhir.r5.profilemodel.PEInstance; -import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; -import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; - - -// Generated by the HAPI Java Profile Generator, Tue, Sep 26, 2023 00:00+1000 +// Generated by the HAPI Java Profile Generator, {date} /** * A complex extension - an extension with 2 levels @@ -48,9 +32,22 @@ public class TestComplexExtension extends PEGeneratedBase { private static final String CANONICAL_URL = "http://hl7.org/fhir/test/StructureDefinition/pe-extension-complex|0.1"; + @Min("1") @Max("*") @Doco("Additional content defined by implementations") + @Definition("May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and managable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.") private List extensions = new ArrayList<>();// @NotNull // Additional content defined by implementations + + @Min("0") @Max("2") @Doco("A code") + @MustSupport(true) + @Definition("A code for the extension") private Coding slice1; // A code + + @Min("0") @Max("*") @Doco("More Details") + @Definition("More details") private List slice2s = new ArrayList<>(); // More Details + + @Min("1") @Max("1") @Doco("Justification Details") + @MustSupport(true) + @Definition("Justification Details.") private Extension slice3;// @NotNull // Justification Details /** diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestDatatypeProfile.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestDatatypeProfile.java index acdad868a..630c49383 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestDatatypeProfile.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestDatatypeProfile.java @@ -12,21 +12,17 @@ import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; -import java.util.List; -import java.util.ArrayList; -import javax.annotation.Nullable; -import java.util.Date; +import org.hl7.fhir.r5.profilemodel.gen.Min; +import org.hl7.fhir.r5.profilemodel.gen.Max; +import org.hl7.fhir.r5.profilemodel.gen.Label; +import org.hl7.fhir.r5.profilemodel.gen.Doco; +import org.hl7.fhir.r5.profilemodel.gen.BindingStrength; +import org.hl7.fhir.r5.profilemodel.gen.ValueSet; +import org.hl7.fhir.r5.profilemodel.gen.MustSupport; +import org.hl7.fhir.r5.profilemodel.gen.Definition; -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.*; -import org.hl7.fhir.r5.profilemodel.PEBuilder; -import org.hl7.fhir.r5.profilemodel.PEInstance; -import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; -import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; - - -// Generated by the HAPI Java Profile Generator, Sun, Aug 20, 2023 19:05+1000 +// Generated by the HAPI Java Profile Generator, {date} /** * Test CodeableConcept Profile. @@ -36,11 +32,23 @@ public class TestDatatypeProfile extends PEGeneratedBase { private static final String CANONICAL_URL = "http://hl7.org/fhir/test/StructureDefinition/pe-profile2|0.1"; + @Min("1") @Max("2") @Doco("Code defined by a terminology system") + @Definition("A reference to a code defined by a terminology system.") private Coding coding;// @NotNull // Code defined by a terminology system + + @Min("1") @Max("1") @Doco("Code defined by a terminology system") + @Definition("A reference to a code defined by a terminology system.") private Coding snomedct;// @NotNull // Code defined by a terminology system + + @Min("0") @Max("1") @Doco("Code defined by a terminology system") + @Definition("A reference to a code defined by a terminology system.") private Coding loinc; // Code defined by a terminology system + + @Min("1") @Max("1") @Doco("Plain text representation of the concept") + @Definition("A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user.") private String text;// @NotNull // Plain text representation of the concept + /** * Parameter-less constructor. If you use this, the fixed values won't be filled * out - they'll be missing. They'll be filled in if/when you call build, so they diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestProfile.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestProfile.java index 996cb0d07..9e7edde7a 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestProfile.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/TestProfile.java @@ -24,9 +24,23 @@ import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; import org.hl7.fhir.r5.profilemodel.gen.PEGeneratedBase; +import org.hl7.fhir.r5.profilemodel.gen.Min; +import org.hl7.fhir.r5.profilemodel.gen.Max; +import org.hl7.fhir.r5.profilemodel.gen.Label; +import org.hl7.fhir.r5.profilemodel.gen.Doco; +import org.hl7.fhir.r5.profilemodel.gen.BindingStrength; +import org.hl7.fhir.r5.profilemodel.gen.ValueSet; +import org.hl7.fhir.r5.profilemodel.gen.MustSupport; +import org.hl7.fhir.r5.profilemodel.gen.Definition; -// Generated by the HAPI Java Profile Generator, Sun, Aug 20, 2023 19:01+1000 +/* +Licensed under CC0 1.0 Universal (CC0 1.0). +The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. +You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. See Other Information below. + */ + +// Generated by the HAPI Java Profile Generator, {date} /** * Test Observation Profile. @@ -36,28 +50,132 @@ public class TestProfile extends PEGeneratedBase { private static final String CANONICAL_URL = "http://hl7.org/fhir/test/StructureDefinition/pe-profile1|0.1"; + public enum ProfileObservationCategoryCode { + LABORATORY, // "Laboratory" = http://terminology.hl7.org/CodeSystem/observation-category#laboratory + IMAGING; // "Imaging" = http://terminology.hl7.org/CodeSystem/observation-category#imaging + + public static ProfileObservationCategoryCode fromCode(String s) { + switch (s) { + case "laboratory": return LABORATORY; + case "imaging": return IMAGING; + default: return null; + } + } + + public static ProfileObservationCategoryCode fromCoding(Coding c) { + if ("http://terminology.hl7.org/CodeSystem/observation-category".equals(c.getSystem()) && "laboratory".equals(c.getCode())) { + return LABORATORY; + } + if ("http://terminology.hl7.org/CodeSystem/observation-category".equals(c.getSystem()) && "imaging".equals(c.getCode())) { + return IMAGING; + } + return null; + } + + public static ProfileObservationCategoryCode fromCodeableConcept(CodeableConcept cc) { + for (Coding c : cc.getCoding()) { + ProfileObservationCategoryCode v = fromCoding(c); + if (v != null) { + return v; + } + } + return null; + } + + public String toDisplay() { + switch (this) { + case LABORATORY: return "Laboratory"; + case IMAGING: return "Imaging"; + default: return null; + } + } + + public String toCode() { + switch (this) { + case LABORATORY: return "laboratory"; + case IMAGING: return "imaging"; + default: return null; + } + } + + public Coding toCoding() { + switch (this) { + case LABORATORY: return new Coding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("laboratory"); + case IMAGING: return new Coding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("imaging"); + default: return null; + } + } + + public CodeableConcept toCodeableConcept() { + Coding c = toCoding(); + return c == null ? null : new CodeableConcept().addCoding(c); + } + } + + @Min("0") @Max("1") @Doco("") private String id; // + + @Min("0") @Max("*") @Doco("Extension") + @Definition("An Extension") private List extensions = new ArrayList<>(); // Extension + + @Min("0") @Max("1") @Doco("A simple extension") + @Definition("A simple extension - an extension with just a value") private String simple; // A simple extension -// @ProfileAnnotation(max = 1, min=1, path="Observation.extension('url')", doco = "blah", type="") + + @Min("0") @Max("1") @Doco("A complex extension") + @Definition("A complex extension - an extension with 2 levels") private TestComplexExtension complex; // A complex extension + + @Min("0") @Max("1") @Doco("Business Identifier for observation") + @Definition("A unique identifier assigned to this observation.") private Identifier identifier; // Business Identifier for observation + + @Min("1") @Max("1") @Doco("registered | preliminary | final | amended +") + @BindingStrength("required") @ValueSet("http://hl7.org/fhir/ValueSet/observation-status|5.0.0") + @Definition("The status of the result value.") private String status;// @NotNull // registered | preliminary | final | amended + + + @Min("1") @Max("1") @Doco("Classification of type of observation") + @BindingStrength("required") @ValueSet("#vs1") + @Definition("A code that classifies the general type of observation being made.") + private ProfileObservationCategoryCode category;// @NotNull // Classification of type of observation + + @Min("1") @Max("1") @Doco("Sexual Orientation") + @BindingStrength("example") @ValueSet("http://hl7.org/fhir/ValueSet/observation-codes") + @Definition("Describes what was observed. Sometimes this is called the observation \"name\".") private CodeableConcept code;// @NotNull // Sexual Orientation + + @Min("1") @Max("1") @Doco("Who and/or what the observation is about") + @MustSupport(true) + @Definition("The patient, or group of patients, location, device, organization, procedure or practitioner this observation is about and into whose or what record the observation is placed. If the actual focus of the observation is different from the subject (or a sample of, part, or region of the subject), the `focus` element or the `code` itself specifies the actual focus of the observation.") private Reference subject;// @NotNull // Who and/or what the observation is about + + @Min("0") @Max("1") @Doco("Healthcare event during which this observation is made") + @Definition("The healthcare event (e.g. a patient and healthcare provider interaction) during which this observation is made.") private Reference encounter; // Healthcare event during which this observation is made + + @Min("1") @Max("1") @Doco("Clinically relevant time/time-period for observation") + @Definition("Time of observation") private Date effective;// @NotNull // Clinically relevant time/time-period for observation + + @Min("0") @Max("*") @Doco("Who is responsible for the observation") + @Definition("Who was responsible for asserting the observed value as \"true\".") private List performers = new ArrayList<>(); // Who is responsible for the observation + + @Min("0") @Max("1") @Doco("Sexual Orientation") + @BindingStrength("extensible") @ValueSet("http://hl7.org/fhir/us/core/ValueSet/us-core-sexual-orientation") + @MustSupport(true) + @Definition("The Sexual Orientation value.") private TestDatatypeProfile valueCodeableConcept; // Sexual Orientation + /** - * Parameter-less constructor. If you use this, the fixed values won't be filled - * out - they'll be missing. They'll be filled in if/when you call build, so they - * won't be missing from the resource, only from this particular object model + * Parameter-less constructor. * */ public TestProfile() { - // todo + initFixedValues(); } /** @@ -65,6 +183,7 @@ public class TestProfile extends PEGeneratedBase { * */ public TestProfile(IWorkerContext context) { + initFixedValues(); workerContext = context; PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.EXTENSION, true); PEInstance src = builder.buildPEInstance(CANONICAL_URL, builder.createResource(CANONICAL_URL, false)); @@ -105,6 +224,9 @@ public class TestProfile extends PEGeneratedBase { if (src.hasChild("status")) { status = src.child("status").asDataType().primitiveValue(); } + if (src.hasChild("category")) { + category = ProfileObservationCategoryCode.fromCodeableConcept((CodeableConcept) src.child("category").asDataType()); + } if (src.hasChild("code")) { code = (CodeableConcept) src.child("code").asDataType(); } @@ -174,7 +296,11 @@ public class TestProfile extends PEGeneratedBase { } tgt.clear("status"); if (status != null) { - tgt.makeChild("status").data().setProperty("value", new StringType(status)); + tgt.makeChild("status").data().setProperty("value", new CodeType(status)); + } + tgt.clear("category"); + if (category != null) { + tgt.addChild("category", category.toCodeableConcept()); } tgt.clear("code"); if (code != null) { @@ -203,6 +329,11 @@ public class TestProfile extends PEGeneratedBase { } + private void initFixedValues() { + status = "final"; + + } + /** * Test Observation Profile. * @@ -309,13 +440,25 @@ public class TestProfile extends PEGeneratedBase { return status; } - public TestProfile setStatus(String value) { - this.status = value; + public boolean hasStatus() { + return true; + } + + /** + * Test Observation Profile. + * + */ + public ProfileObservationCategoryCode getCategory() { + return category; + } + + public TestProfile setCategory(ProfileObservationCategoryCode value) { + this.category = value; return this; } - public boolean hasStatus() { - return status != null; + public boolean hasCategory() { + return category != null; } /** @@ -438,6 +581,7 @@ public class TestProfile extends PEGeneratedBase { complex = null; identifier = null; status = null; + category = null; code = null; subject = null; encounter = null; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index c98e2adc1..43cd93f75 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -109,6 +109,12 @@ public class Utilities { return inf.pluralize(word); } + public static String singularise(String word) { + Inflector inf = new Inflector(); + return inf.singularize(word); + } + + public static boolean isInteger(String string) { if (isBlank(string)) { return false; @@ -556,6 +562,8 @@ public class Utilities { return b.toString(); } + + public static String unescapeJson(String json) throws FHIRException { if (json == null) return null; @@ -991,6 +999,23 @@ public class Utilities { } + public static String escapeCSV(String value) { + if (value == null) + return ""; + + StringBuilder b = new StringBuilder(); + for (char c : value.toCharArray()) { + if (c == '"') + b.append("\"\""); + else if (isWhitespace(c)) + b.append(" "); + else + b.append(c); + } + return b.toString(); + } + + public static String escapeJson(String value) { if (value == null) return ""; From 103984c10e69340c0984f54ad5125f358fa8ef36 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 24 Oct 2023 17:37:01 +1100 Subject: [PATCH 4/8] Fix issue where .resolve() in FHIRPath didn't work with URL values (and fix typo in i18n system) --- .../main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java | 2 +- .../main/java/org/hl7/fhir/r5/utils/ResourceSorters.java | 8 +++++++- .../java/org/hl7/fhir/utilities/i18n/I18nConstants.java | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index b51bc171d..3c89a1901 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -3632,7 +3632,7 @@ public class FHIRPathEngine { } private void checkContextReference(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { - if (!focus.hasType(worker, "string") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Reference") && !focus.hasType(worker, "canonical")) { + if (!focus.hasType(worker, "string") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "url") && !focus.hasType(worker, "Reference") && !focus.hasType(worker, "canonical")) { throw makeException(expr, I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java index 81ad8906f..fa9608a6b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceSorters.java @@ -10,7 +10,13 @@ public class ResourceSorters { @Override public int compare(CanonicalResource arg0, CanonicalResource arg1) { - return arg0.getUrl().compareTo(arg1.getUrl()); + if (arg0.getUrl() != null) { + return arg0.getUrl().compareTo(arg1.getUrl()); + } else if (arg1.getUrl() != null) { + return -arg1.getUrl().compareTo(arg0.getUrl()); + } else { + return 0; + } } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index a8c246775..bc66ea886 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -177,7 +177,7 @@ public class I18nConstants { public static final String FHIRPATH_ORDERED_ONLY = "FHIRPATH_ORDERED_ONLY"; public static final String FHIRPATH_PARAM_WRONG = "FHIRPATH_PARAM_WRONG"; public static final String FHIRPATH_PRIMITIVE_ONLY = "FHIRPATH_PRIMITIVE_ONLY"; - public static final String FHIRPATH_REFERENCE_ONLY = "FHIRPATH_ORDERED_ONLY"; + public static final String FHIRPATH_REFERENCE_ONLY = "FHIRPATH_REFERENCE_ONLY"; public static final String FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND = "FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND"; public static final String FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET = "FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET"; public static final String FHIRPATH_RIGHT_VALUE = "FHIRPATH_RIGHT_VALUE"; From 74a5c4cf434ca92a8d5289cc1ba4292ffe5ceb8b Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 24 Oct 2023 17:47:48 +1100 Subject: [PATCH 5/8] release notes --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3d178e66a..889c801e6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,12 +5,16 @@ * Update ViewDefinition validator for change (alias -> name) * Fix for NPE validating sql-on-fhir ViewDefinition * Fix for index out of bounds error when extension uses itself +* Fix issue where .resolve() in FHIRPath didn't work with URL values (and fix typo in i18n system) +* Implement FHIRPath slice() function in validator ## Other code changes +* Breaking API Change: Revise FHIRPath API so hosts can evaluate expressions in custom functions * Add package use tracking to FHIR cache for validator.fhir.org * Support for instance-name and instance-description in IG publisher * Element.removeExtension (support for instance-name and instance-description extensions in IG publisher) * Split terminology service tests * Hack for wrong URLs in subscriptions backport * Remove dependencies for unused UI experiment +* More improvements to profile code generation From 3ea42ca1b0967f4737898980f76e07000644fe59 Mon Sep 17 00:00:00 2001 From: dotasek Date: Tue, 24 Oct 2023 14:29:05 -0400 Subject: [PATCH 6/8] Fix test ignore/exclude issues, bump test cases --- .../hl7/fhir/dstu2/test/TestingUtilities.java | 4 +++ .../dstu2016may/test/TestingUtilities.java | 5 +++ .../dstu3/test/support/TestingUtilities.java | 5 ++- .../fhir/r4/test/utils/TestingUtilities.java | 4 +++ .../fhir/r4b/test/utils/TestingUtilities.java | 9 +++--- .../fhir/r5/test/utils/TestingUtilities.java | 5 ++- .../tests/LocalTerminologyServiceTests.java | 31 +++++++++++-------- .../tests/utilities/TestUtilities.java | 5 +++ pom.xml | 7 ++++- 9 files changed, 55 insertions(+), 20 deletions(-) diff --git a/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java index 5a0e65f5f..1caa5cc2f 100644 --- a/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java +++ b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java @@ -5,6 +5,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.DocumentBuilder; @@ -245,4 +246,7 @@ public class TestingUtilities { return null; } + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java b/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java index a6c297ee3..645df0f83 100644 --- a/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java +++ b/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java @@ -5,6 +5,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.DocumentBuilder; @@ -245,4 +246,8 @@ public class TestingUtilities { return null; } + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java index 860617993..ea23d05cb 100644 --- a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java +++ b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.DocumentBuilder; @@ -276,5 +277,7 @@ public class TestingUtilities extends BaseTestingUtilities { return null; } - + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java index c11cf89ec..b71cd6662 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java @@ -38,6 +38,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.DocumentBuilder; @@ -528,4 +529,7 @@ public class TestingUtilities { } } + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java index ac718895e..1940a103a 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java @@ -5,10 +5,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -499,4 +496,8 @@ public class TestingUtilities extends BaseTestingUtilities { return path; } } + + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java index 1039d3b73..42a61d208 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import org.fhir.ucum.UcumEssenceService; @@ -175,5 +176,7 @@ public class TestingUtilities extends BaseTestingUtilities { FilesystemPackageCacheManager.setPackageProvider(new TestingUtilities.PackageProvider()); } - + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java index 91db02a4c..23e6c263c 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java @@ -4,11 +4,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.apache.commons.io.IOUtils; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; @@ -27,11 +23,9 @@ import org.hl7.fhir.utilities.json.model.JsonObject; import org.hl7.fhir.utilities.settings.FhirSettings; import org.hl7.fhir.validation.special.TxTester; import org.hl7.fhir.validation.special.TxTester.ITxTesterLoader; +import org.hl7.fhir.validation.tests.utilities.TestUtilities; import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.condition.EnabledIf; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -39,8 +33,7 @@ import org.junit.runners.Parameterized.Parameters; import com.google.common.base.Charsets; @RunWith(Parameterized.class) -@EnabledIf("localTxRunning") -@Disabled + public class LocalTerminologyServiceTests implements ITxTesterLoader { public static class JsonObjectPair { @@ -60,6 +53,8 @@ public class LocalTerminologyServiceTests implements ITxTesterLoader { return new File("/Users/grahamegrieve/work/server/serverx").exists(); } + + @Parameters(name = "{index}: id {0}") public static Iterable data() throws IOException { @@ -99,10 +94,16 @@ public class LocalTerminologyServiceTests implements ITxTesterLoader { } @SuppressWarnings("deprecation") - @Test - @Tag("excludedInSurefire") - @Disabled + @Test public void test() throws Exception { + if (TestUtilities.runningAsSurefire()) { + logTestSkip("Running in surefire."); + return; + } + if (!localTxRunning()) { + logTestSkip("No local terminology server available."); + return; + } if (SERVER != null) { if (tester == null) { tester = new TxTester(this, SERVER, true, externals); @@ -114,6 +115,10 @@ public class LocalTerminologyServiceTests implements ITxTesterLoader { } } + private void logTestSkip(String reason) { + System.out.println("Skipping test: " + setup.suite.asString("name") + " " + setup.test.asString("name") + " reason: " + reason); + } + public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { String contents = TestingUtilities.loadTestResource("tx", filename); try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java index 235a6f8ab..3187695a0 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java @@ -1,6 +1,7 @@ package org.hl7.fhir.validation.tests.utilities; import java.nio.file.Paths; +import java.util.Locale; import org.hl7.fhir.r5.context.TerminologyCache; import org.hl7.fhir.r5.test.utils.TestingUtilities; @@ -65,4 +66,8 @@ public class TestUtilities { return validationEngine; } + + public static boolean runningAsSurefire() { + return "true".equals(System.getProperty("runningAsSurefire") != null ? System.getProperty("runningAsSurefire").toLowerCase(Locale.ENGLISH) : ""); + } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index c939ec634..8c92381c4 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 32.0.1-jre 6.4.1 - 1.4.12-SNAPSHOT + 1.4.13-SNAPSHOT 2.15.2 5.9.2 1.8.2 @@ -341,8 +341,13 @@ @{argLine} -Xmx5632m COMPAT + + true false + + excludedInSurefire From 3e98c25cc284668f33bd6e18d2e4d8655418ee59 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 25 Oct 2023 06:23:49 +1100 Subject: [PATCH 7/8] Fix bug processing snapshot --- .../hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java index cc183f65d..58c20fa1a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java @@ -165,7 +165,7 @@ public class ProfilePathProcessor { ElementDefinition res = null; List typeList = new ArrayList<>(); // just repeat processing entries until we run out of our allowed scope (1st entry, the allowed scope is all the entries) - while (cursors.baseCursor <= getBaseLimit()) { + while (cursors.baseCursor <= getBaseLimit() && cursors.baseCursor < cursors.base.getElement().size()) { // get the current focus of the base, and decide what to do ElementDefinition currentBase = cursors.base.getElement().get(cursors.baseCursor); String currentBasePath = profileUtilities.fixedPathSource(getContextPathSource(), currentBase.getPath(), getRedirector()); From 238793b4a47c2d0ccd05e9dfb0bedcc08560d900 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 25 Oct 2023 06:44:11 +1100 Subject: [PATCH 8/8] rename union to unionAll --- .../main/java/org/hl7/fhir/r5/utils/sql/Runner.java | 4 ++-- .../main/java/org/hl7/fhir/r5/utils/sql/Validator.java | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java index 1e1a7671f..5087a497d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Runner.java @@ -135,7 +135,7 @@ public class Runner implements IEvaluationContext { focus.add(b); } -// } else if (select.has("union")) { +// } else if (select.has("unionAll")) { // focus.addAll(executeUnion(select, b)); List> tempRows = new ArrayList<>(); @@ -149,7 +149,7 @@ public class Runner implements IEvaluationContext { executeColumn(column, f, rowsToAdd); } - for (JsonObject sub : select.getJsonObjects("union")) { + for (JsonObject sub : select.getJsonObjects("unionAll")) { executeSelect(sub, f, rowsToAdd); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java index ba5b90db6..7aa5cc8f9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java @@ -106,8 +106,8 @@ public class Validator { checkForEach(path, viewDefinition, viewDefinition.get("forEach"), t); } else if (viewDefinition.has("forEachOrNull")) { checkForEachOrNull(path, viewDefinition, viewDefinition.get("forEachOrNull"), t); - } else if (viewDefinition.has("union")) { - checkUnion(path, viewDefinition, viewDefinition.get("union"), t); + } else if (viewDefinition.has("unionAll")) { + checkUnion(path, viewDefinition, viewDefinition.get("unionAll"), t); } else { i = 0; if (checkAllObjects(path, viewDefinition, "select")) { @@ -135,8 +135,8 @@ public class Validator { if (t != null) { boolean content = false; - if (select.has("union")) { - content = checkUnion(path, select, select.get("union"), t); + if (select.has("unionAll")) { + content = checkUnion(path, select, select.get("unionAll"), t); } if (select.has("column")) { @@ -181,7 +181,7 @@ public class Validator { private boolean checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) { - JsonElement a = focus.get("union"); + JsonElement a = focus.get("unionAll"); if (!(a instanceof JsonArray)) { error(path+".union", a, "union is not an array", IssueType.INVALID); return false;