From 7b7af6087ebf28d2203eda1df6748140998c4e78 Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 10:29:18 +1000 Subject: [PATCH 1/7] base components of implementing the defineVariable fhirpath function --- .../hl7/fhir/r5/fhirpath/ExpressionNode.java | 4 +- .../hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 99 ++++++++++++++++++- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../src/main/resources/Messages.properties | 1 + 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/ExpressionNode.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/ExpressionNode.java index c1850437c..1571c7407 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/ExpressionNode.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/ExpressionNode.java @@ -51,7 +51,7 @@ public class ExpressionNode { Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single, First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, MatchesFull, ReplaceMatches, Contains, Replace, Length, - Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue, + Children, Descendants, MemberOf, Trace, DefineVariable, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue, HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToDate, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo, Round, Sqrt, Abs, Ceiling, Exp, Floor, Ln, Log, Power, Truncate, @@ -106,6 +106,7 @@ public class ExpressionNode { if (name.equals("descendants")) return Function.Descendants; if (name.equals("memberOf")) return Function.MemberOf; if (name.equals("trace")) return Function.Trace; + if (name.equals("defineVariable")) return Function.DefineVariable; if (name.equals("check")) return Function.Check; if (name.equals("today")) return Function.Today; if (name.equals("now")) return Function.Now; @@ -211,6 +212,7 @@ public class ExpressionNode { case Descendants : return "descendants"; case MemberOf : return "memberOf"; case Trace : return "trace"; + case DefineVariable : return "defineVariable"; case Check : return "check"; case Today : return "today"; case Now : return "now"; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 666799c25..8a8d087ca 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -193,6 +193,7 @@ public class FHIRPathEngine { /** * A constant reference - e.g. a reference to a name that must be resolved in context. * The % will be removed from the constant name before this is invoked. + * Variables created with defineVariable will not be processed by resolveConstant (or resolveConstantType) * * This will also be called if the host invokes the FluentPath engine with a context of null * @@ -998,6 +999,7 @@ public class FHIRPathEngine { private List total; private Map aliases; private int index; + private Map> definedVariables; public ExecutionContext(Object appInfo, Base resource, Base rootResource, Base context, Map aliases, Base thisItem) { this.appInfo = appInfo; @@ -1046,6 +1048,19 @@ public class FHIRPathEngine { index = i; return this; } + + public boolean hasDefinedVariable(String name) { + return definedVariables != null && definedVariables.containsKey(name); + } + public List getDefinedVariable(String name) { + return definedVariables == null ? makeNull() : definedVariables.get(name); + } + public void setDefinedVariable(String name, List value) { + if (definedVariables == null) { + definedVariables = new HashMap>(); + } + definedVariables.put(name, value); + } } private static class ExecutionTypeContext { @@ -1054,7 +1069,7 @@ public class FHIRPathEngine { private TypeDetails context; private TypeDetails thisItem; private TypeDetails total; - + private Map definedVariables; public ExecutionTypeContext(Object appInfo, String resource, TypeDetails context, TypeDetails thisItem) { super(); @@ -1071,7 +1086,24 @@ public class FHIRPathEngine { return thisItem; } + public boolean hasDefinedVariable(String name) { + return definedVariables != null && definedVariables.containsKey(name); + } + public TypeDetails getDefinedVariable(String name) { + return definedVariables == null ? null : definedVariables.get(name); + } + public void setDefinedVariable(String name, TypeDetails value) { + if (definedVariables == null) { + definedVariables = new HashMap(); + } else { + if (definedVariables.containsKey(name)) { + // Can't do this, so throw an error + throw new PathEngineException("Redefine of variable "+name, I18nConstants.FHIRPATH_REDEFINE_VARIABLE); + } + } + definedVariables.put(name, value); + } } private ExpressionNode parseExpression(FHIRLexer lexer, boolean proximal) throws FHIRLexerException { @@ -1411,6 +1443,7 @@ public class FHIRPathEngine { case Descendants: return checkParamCount(lexer, location, exp, 0); case MemberOf: return checkParamCount(lexer, location, exp, 1); case Trace: return checkParamCount(lexer, location, exp, 1, 2); + case DefineVariable: return checkParamCount(lexer, location, exp, 1, 2); case Check: return checkParamCount(lexer, location, exp, 2); case Today: return checkParamCount(lexer, location, exp, 0); case Now: return checkParamCount(lexer, location, exp, 0); @@ -1664,6 +1697,10 @@ public class FHIRPathEngine { } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { + String varName = c.getValue().substring(1); + if (context.hasDefinedVariable(varName)) { + return context.getDefinedVariable(varName); + } 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))); @@ -3139,6 +3176,10 @@ public class FHIRPathEngine { } else if (hostServices == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { + String varName = s.substring(1); + if (context.hasDefinedVariable(varName)) { + return context.getDefinedVariable(varName); + } TypeDetails v = hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant); if (v == null) { throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); @@ -3722,7 +3763,7 @@ public class FHIRPathEngine { case 0: return exp.getFunction() == Function.Where || exp.getFunction() == Function.Exists || exp.getFunction() == Function.All || exp.getFunction() == Function.Select || exp.getFunction() == Function.Repeat || exp.getFunction() == Function.Aggregate; case 1: - return exp.getFunction() == Function.Trace; + return exp.getFunction() == Function.Trace || exp.getFunction() == Function.DefineVariable; default: return false; } @@ -3875,6 +3916,7 @@ public class FHIRPathEngine { case Descendants : return funcDescendants(context, focus, exp); case MemberOf : return funcMemberOf(context, focus, exp); case Trace : return funcTrace(context, focus, exp); + case DefineVariable : return funcDefineVariable(context, focus, exp); case Check : return funcCheck(context, focus, exp); case Today : return funcToday(context, focus, exp); case Now : return funcNow(context, focus, exp); @@ -4640,13 +4682,48 @@ public class FHIRPathEngine { private ExecutionContext changeThis(ExecutionContext context, Base newThis) { - return new ExecutionContext(context.appInfo, context.focusResource, context.rootResource, context.context, context.aliases, newThis); + ExecutionContext newContext = new ExecutionContext(context.appInfo, context.focusResource, context.rootResource, context.context, context.aliases, newThis); + // append all of the defined variables from the context into the new context + if (context.definedVariables != null) { + for (String s : context.definedVariables.keySet()) { + newContext.setDefinedVariable(s, context.definedVariables.get(s)); + } + } + return newContext; + } + + private ExecutionContext contextForParameter(ExecutionContext context) { + ExecutionContext newContext = new ExecutionContext(context.appInfo, context.focusResource, context.rootResource, context.context, context.aliases, context.thisItem); + // append all of the defined variables from the context into the new context + if (context.definedVariables != null) { + for (String s : context.definedVariables.keySet()) { + newContext.setDefinedVariable(s, context.definedVariables.get(s)); + } + } + return newContext; } private ExecutionTypeContext changeThis(ExecutionTypeContext context, TypeDetails newThis) { - return new ExecutionTypeContext(context.appInfo, context.resource, context.context, newThis); + ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, newThis); + // append all of the defined variables from the context into the new context + if (context.definedVariables != null) { + for (String s : context.definedVariables.keySet()) { + newContext.setDefinedVariable(s, context.definedVariables.get(s)); + } + } + return newContext; } + private ExecutionTypeContext contextForParameter(ExecutionTypeContext context) { + ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, context.thisItem); + // append all of the defined variables from the context into the new context + if (context.definedVariables != null) { + for (String s : context.definedVariables.keySet()) { + newContext.setDefinedVariable(s, context.definedVariables.get(s)); + } + } + return newContext; + } private List funcNow(ExecutionContext context, List focus, ExpressionNode exp) { List result = new ArrayList(); @@ -5484,6 +5561,20 @@ public class FHIRPathEngine { return focus; } + private List funcDefineVariable(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + List nl = execute(context, focus, exp.getParameters().get(0), true); + String name = nl.get(0).primitiveValue(); + List value; + if (exp.getParameters().size() == 2) { + value = execute(context, focus, exp.getParameters().get(1), true); + } else { + value = focus; + } + // stash the variable into the context + context.setDefinedVariable(name, value); + return focus; + } + private List funcCheck(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException { List n1 = execute(context, focus, expr.getParameters().get(0), true); if (!convertToBoolean(n1)) { 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 7968cb2e7..9e481a680 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 @@ -1102,6 +1102,7 @@ public class I18nConstants { public static final String SD_ELEMENT_FIXED_WRONG_TYPE = "SD_ELEMENT_FIXED_WRONG_TYPE"; public static final String SD_ELEMENT_REASON_DERIVED = "SD_ELEMENT_REASON_DERIVED"; public static final String SD_ELEMENT_PATTERN_WRONG_TYPE = "SD_ELEMENT_PATTERN_WRONG_TYPE"; + public static final String FHIRPATH_REDEFINE_VARIABLE = "FHIRPATH_REDEFINE_VARIABLE"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 7002b84bd..5dabcf717 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1084,6 +1084,7 @@ TX_GENERAL_CC_ERROR_MESSAGE = No valid coding was found for the value set ''{0}' Validation_VAL_Profile_Minimum_SLICE_one = Slice ''{3}'': a matching slice is required, but not found (from {1}). Note that other slices are allowed in addition to this required slice Validation_VAL_Profile_Minimum_SLICE_other = Slice ''{3}'': minimum required = {0}, but only found {7} (from {1}) FHIRPATH_UNKNOWN_EXTENSION = Reference to an unknown extension - double check that the URL ''{0}'' is correct +FHIRPATH_REDEFINE_VARIABLE = The variable ''{0}'' cannot be redefined Type_Specific_Checks_DT_XHTML_Resolve = Hyperlink ''{0}'' at ''{1}'' for ''{2}''' does not resolve Type_Specific_Checks_DT_XHTML_Resolve_Img = Image source ''{0}'' at ''{1}'' does not resolve TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES = Hyperlink ''{0}'' at ''{1}'' for ''{2}'' resolves to multiple targets ({3}) From 5d612c78b25f0f3877d0741191f50d715782ffe3 Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 11:55:30 +1000 Subject: [PATCH 2/7] Don't permit variables to be over-written. --- .../hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 8a8d087ca..a36c6c2b5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -1056,9 +1056,18 @@ public class FHIRPathEngine { return definedVariables == null ? makeNull() : definedVariables.get(name); } public void setDefinedVariable(String name, List value) { + if (isSystemVariable(name)) + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_REDEFINE_VARIABLE, name), I18nConstants.FHIRPATH_REDEFINE_VARIABLE); + if (definedVariables == null) { definedVariables = new HashMap>(); + } else { + if (definedVariables.containsKey(name)) { + // Can't do this, so throw an error + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_REDEFINE_VARIABLE, name), I18nConstants.FHIRPATH_REDEFINE_VARIABLE); + } } + definedVariables.put(name, value); } } @@ -1093,6 +1102,9 @@ public class FHIRPathEngine { return definedVariables == null ? null : definedVariables.get(name); } public void setDefinedVariable(String name, TypeDetails value) { + if (isSystemVariable(name)) + throw new PathEngineException("Redefine of variable "+name, I18nConstants.FHIRPATH_REDEFINE_VARIABLE); + if (definedVariables == null) { definedVariables = new HashMap(); } else { @@ -1771,6 +1783,21 @@ public class FHIRPathEngine { } } + static boolean isSystemVariable(String name){ + if (name.equals("sct")) + return true; + if (name.equals("loinc")) + return true; + if (name.equals("ucum")) + return true; + if (name.equals("resource")) + return true; + if (name.equals("rootResource")) + return true; + if (name.equals("context")) + return true; + return false; + } private List resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr, boolean explicitConstant) throws PathEngineException { if (s.equals("%sct")) { From d65f2cc47d5ac21ae484353ada19d14b16294510 Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 12:22:28 +1000 Subject: [PATCH 3/7] Static check the parameters for the defineVariable function. --- .../hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index a36c6c2b5..086e31f13 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -3559,6 +3559,25 @@ public class FHIRPathEngine { checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } + case DefineVariable : { + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.UNORDERED, TypeDetails.FP_String)); + // set the type of the variable + // Actually evaluate the value of the first parameter (to get the name of the variable if possible) + // and if have that, set it into the context + ExpressionNode p = exp.getParameters().get(0); + if (p.getKind() == Kind.Constant && p.getConstant() != null) { + String varName = exp.getParameters().get(0).getConstant().primitiveValue(); + if (varName != null) { + if (paramTypes.size() > 1) + context.setDefinedVariable(varName, paramTypes.get(1)); + else + context.setDefinedVariable(varName, focus); + } + } else { + // this variable is not a constant, so we can't analyze what name it could have + } + return focus; + } case Check : { checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; From f54ae79949d56355f5894371542d8173ce96cc8a Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 13:21:00 +1000 Subject: [PATCH 4/7] set the context to isolate parameters to functions/operations from sharing the same defineVariables context --- .../java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 086e31f13..74fd1aca6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -1513,8 +1513,9 @@ public class FHIRPathEngine { return false; } - private List execute(ExecutionContext context, List focus, ExpressionNode exp, boolean atEntry) throws FHIRException { + private List execute(ExecutionContext inContext, List focus, ExpressionNode exp, boolean atEntry) throws FHIRException { // System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString()); + ExecutionContext context = contextForParameter(inContext); List work = new ArrayList(); switch (exp.getKind()) { case Unary: @@ -1558,6 +1559,7 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { + context = contextForParameter(inContext); List work2 = preOperate(work, last.getOperation(), exp); if (work2 != null) { work = work2; @@ -1621,8 +1623,8 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, exp.getName()); } - private TypeDetails executeType(ExecutionTypeContext context, TypeDetails focus, ExpressionNode exp, Set elementDependencies, boolean atEntry, boolean canBeNone, ExpressionNode container) throws PathEngineException, DefinitionException { - + private TypeDetails executeType(ExecutionTypeContext inContext, TypeDetails focus, ExpressionNode exp, Set elementDependencies, boolean atEntry, boolean canBeNone, ExpressionNode container) throws PathEngineException, DefinitionException { + ExecutionTypeContext context = contextForParameter(inContext); TypeDetails result = new TypeDetails(null); switch (exp.getKind()) { case Name: @@ -1672,6 +1674,7 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { + context = contextForParameter(inContext); TypeDetails work; if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { work = executeTypeName(context, focus, next, atEntry); From af6759dd4c7b53e1f266c900791ccf6d9aa3d310 Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 13:57:36 +1000 Subject: [PATCH 5/7] Add support for a test to skip the static check (only case has been a declareVariable that uses a dynamically calculated name) --- .../org/hl7/fhir/r5/test/FHIRPathTests.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) 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 aefedf7f6..0cdf20a34 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 @@ -180,6 +180,9 @@ public class FHIRPathTests { fail = TestResultType.EXECUTION; }; fp.setAllowPolymorphicNames("lenient/polymorphics".equals(test.getAttribute("mode"))); + boolean skipStaticCheck = false; + if ("true".equals(test.getAttribute("skipStaticCheck"))) + skipStaticCheck = true; Base res = null; List outcome = new ArrayList(); @@ -210,17 +213,19 @@ public class FHIRPathTests { } } - try { - if (Utilities.noString(input)) { - fp.check(null, null, node); - } else { - fp.check(res, res.fhirType(), res.fhirType(), node); + if (!skipStaticCheck) { + try { + if (Utilities.noString(input)) { + fp.check(null, null, node); + } else { + fp.check(res, res.fhirType(), res.fhirType(), node); + } + Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression)); + } catch (Exception e) { + System.out.println("Checking Error: "+e.getMessage()); + Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression)); + node = null; } - Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression)); - } catch (Exception e) { - System.out.println("Checking Error: "+e.getMessage()); - Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression)); - node = null; } } From 33d9304700aadb7b805095db756b2c6265e75720 Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Fri, 12 Apr 2024 16:06:03 +1000 Subject: [PATCH 6/7] Missed the total and index copying to the child context. --- .../src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index 74fd1aca6..a8399b4f5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -4743,6 +4743,8 @@ public class FHIRPathEngine { private ExecutionContext contextForParameter(ExecutionContext context) { ExecutionContext newContext = new ExecutionContext(context.appInfo, context.focusResource, context.rootResource, context.context, context.aliases, context.thisItem); + newContext.total = context.total; + newContext.index = context.index; // append all of the defined variables from the context into the new context if (context.definedVariables != null) { for (String s : context.definedVariables.keySet()) { From 49809ec6c262d45856c2e6991bc3a91b957abafb Mon Sep 17 00:00:00 2001 From: Brian Postlethwaite Date: Mon, 15 Apr 2024 10:38:25 +1000 Subject: [PATCH 7/7] The scoping for the `aliasAs` function has different needs to everything else, so has minor workaround. --- .../src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java index a8399b4f5..0b9f0d3ee 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java @@ -1540,7 +1540,7 @@ public class FHIRPathEngine { } break; case Function: - List work2 = evaluateFunction(context, focus, exp); + List work2 = evaluateFunction("aliasAs".equals(exp.getName()) ? inContext : context, focus, exp); work.addAll(work2); break; case Constant: