From 46d1987cd0322d13846a8038b0b279324eed94d9 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 30 Sep 2020 12:10:59 +1000 Subject: [PATCH] refactor FHIRPath to report error locations for run time errors --- .../org/hl7/fhir/r4/model/ExpressionNode.java | 26 +- .../java/org/hl7/fhir/r4/utils/FHIRLexer.java | 2 +- .../org/hl7/fhir/r4/utils/FHIRPathEngine.java | 493 ++++++------- .../org/hl7/fhir/r5/model/ExpressionNode.java | 25 +- .../java/org/hl7/fhir/r5/utils/FHIRLexer.java | 2 +- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 696 +++++++++--------- .../org/hl7/fhir/r5/test/FHIRPathTests.java | 3 + .../fhir/exceptions/PathEngineException.java | 59 +- .../hl7/fhir/utilities/SourceLocation.java | 27 + 9 files changed, 684 insertions(+), 649 deletions(-) create mode 100644 org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java index a061937a9..3ed27a087 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java @@ -34,6 +34,7 @@ package org.hl7.fhir.r4.model; import java.util.ArrayList; import java.util.List; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; public class ExpressionNode { @@ -41,31 +42,6 @@ public class ExpressionNode { public enum Kind { Name, Function, Constant, Group, Unary } - public static class SourceLocation { - private int line; - private int column; - public SourceLocation(int line, int column) { - super(); - this.line = line; - this.column = column; - } - public int getLine() { - return line; - } - public int getColumn() { - return column; - } - public void setLine(int line) { - this.line = line; - } - public void setColumn(int column) { - this.column = column; - } - - public String toString() { - return Integer.toString(line)+", "+Integer.toString(column); - } - } public enum Function { Custom, diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java index 38df5e9a4..83619c66b 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java @@ -34,7 +34,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.ExpressionNode; -import org.hl7.fhir.r4.model.ExpressionNode.SourceLocation; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; // shared lexer for concrete syntaxes 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 89f805006..7bc7ec1c0 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 @@ -19,6 +19,7 @@ import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r4.model.TypeDetails.ProfiledType; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; @@ -414,10 +415,10 @@ public class FHIRPathEngine { } StructureDefinition sd = worker.fetchResource(StructureDefinition.class, ctxt); if (sd == null) - throw new PathEngineException("Unknown context "+context); - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + throw new PathEngineException("Unknown context "+context, expr.getStart(), expr.toString()); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) - throw new PathEngineException("Unknown context element "+context); + throw new PathEngineException("Unknown context element "+context, expr.getStart(), expr.toString()); if (ed.fixedType != null) types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) @@ -438,9 +439,9 @@ public class FHIRPathEngine { if (!context.contains(".")) { types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); } else { - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) - throw new PathEngineException("Unknown context element "+context); + throw new PathEngineException("Unknown context element "+context, expr.getStart(), expr.toString()); if (ed.fixedType != null) types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) @@ -1161,7 +1162,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - Base b = resolveConstant(context, exp.getConstant(), false); + Base b = resolveConstant(context, exp.getConstant(), false, exp); if (b != null) work.add(b); break; @@ -1177,15 +1178,15 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { - List work2 = preOperate(work, last.getOperation()); + List work2 = preOperate(work, last.getOperation(), exp); if (work2 != null) work = work2; else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { work2 = executeTypeName(context, focus, next, false); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); } else { work2 = execute(context, focus, next, true); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); // System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString()); } last = next; @@ -1206,7 +1207,7 @@ public class FHIRPathEngine { } - private List preOperate(List left, Operation operation) throws PathEngineException { + private List preOperate(List left, Operation operation, ExpressionNode expr) throws PathEngineException { if (left.size() == 0) return null; switch (operation) { @@ -1215,7 +1216,7 @@ public class FHIRPathEngine { case Or: return isBoolean(left, true) ? makeBoolean(true) : null; case Implies: - Equality v = asBool(left); + Equality v = asBool(left, expr); return v == Equality.False ? makeBoolean(true) : null; default: return null; @@ -1246,13 +1247,13 @@ public class FHIRPathEngine { else if (atEntry && exp.getName().equals("$total")) result.update(anything(CollectionStatus.UNORDERED)); else if (atEntry && focus == null) - result.update(executeContextType(context, exp.getName())); + result.update(executeContextType(context, exp.getName(), exp)); else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); } if (result.hasNoTypes()) - throw new PathEngineException("The name "+exp.getName()+" is not valid for any of the possible types: "+focus.describe()); + throw new PathEngineException("The name "+exp.getName()+" is not valid for any of the possible types: "+focus.describe(), exp.getStart(), exp.toString()); } break; case Function: @@ -1262,7 +1263,7 @@ public class FHIRPathEngine { result.addType("integer"); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant())); + result.update(resolveConstantType(context, exp.getConstant(), exp)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1282,7 +1283,7 @@ public class FHIRPathEngine { work = executeTypeName(context, focus, next, atEntry); else work = executeType(context, focus, next, atEntry); - result = operateTypes(result, last.getOperation(), work); + result = operateTypes(result, last.getOperation(), work, exp); last = next; next = next.getOpNext(); } @@ -1291,16 +1292,16 @@ public class FHIRPathEngine { return result; } - private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (!(constant instanceof FHIRConstant)) return constant; FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext); + return resolveConstant(context, c.getValue(), beforeContext, expr); } else if (c.getValue().startsWith("@")) { return processDateConstant(context.appInfo, c.getValue().substring(1)); } else - throw new PathEngineException("Invaild FHIR Constant "+c.getValue()); + throw new PathEngineException("Invaild FHIR Constant "+c.getValue(), expr.getStart(), expr.toString()); } private Base processDateConstant(Object appInfo, String value) throws PathEngineException { @@ -1322,7 +1323,7 @@ public class FHIRPathEngine { } - private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (s.equals("%sct")) return new StringType("http://snomed.info/sct").noExtensions(); else if (s.equals("%loinc")) @@ -1331,11 +1332,11 @@ public class FHIRPathEngine { return new StringType("http://unitsofmeasure.org").noExtensions(); else if (s.equals("%resource")) { if (context.focusResource == null) - throw new PathEngineException("Cannot use %resource in this context"); + throw new PathEngineException("Cannot use %resource in this context", expr.getStart(), expr.toString()); return context.focusResource; } else if (s.equals("%rootResource")) { if (context.rootResource == null) - throw new PathEngineException("Cannot use %rootResource in this context"); + throw new PathEngineException("Cannot use %rootResource in this context", expr.getStart(), expr.toString()); return context.rootResource; } else if (s.equals("%context")) { return context.context; @@ -1348,7 +1349,7 @@ public class FHIRPathEngine { else if (s.startsWith("%`ext-")) return new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions(); else if (hostServices == null) - throw new PathEngineException("Unknown fixed constant '"+s+"'"); + throw new PathEngineException("Unknown fixed constant '"+s+"'", expr.getStart(), expr.toString()); else return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); } @@ -1408,39 +1409,39 @@ public class FHIRPathEngine { } - private List operate(ExecutionContext context, List left, Operation operation, List right) throws FHIRException { + private List operate(ExecutionContext context, List left, Operation operation, List right, ExpressionNode expr) throws FHIRException { switch (operation) { case Equals: return opEquals(left, right); - case Equivalent: return opEquivalent(left, right); - case NotEquals: return opNotEquals(left, right); - case NotEquivalent: return opNotEquivalent(left, right); - case LessThan: return opLessThan(left, right); - case Greater: return opGreater(left, right); - case LessOrEqual: return opLessOrEqual(left, right); - case GreaterOrEqual: return opGreaterOrEqual(left, right); - case Union: return opUnion(left, right); - case In: return opIn(left, right); - case MemberOf: return opMemberOf(context, left, right); - case Contains: return opContains(left, right); - case Or: return opOr(left, right); - case And: return opAnd(left, right); - case Xor: return opXor(left, right); - case Implies: return opImplies(left, right); - case Plus: return opPlus(left, right); - case Times: return opTimes(left, right); - case Minus: return opMinus(left, right); - case Concatenate: return opConcatenate(left, right); - case DivideBy: return opDivideBy(left, right); - case Div: return opDiv(left, right); - case Mod: return opMod(left, right); - case Is: return opIs(left, right); - case As: return opAs(left, right); + case Equivalent: return opEquivalent(left, right, expr); + case NotEquals: return opNotEquals(left, right, expr); + case NotEquivalent: return opNotEquivalent(left, right, expr); + case LessThan: return opLessThan(left, right, expr); + case Greater: return opGreater(left, right, expr); + case LessOrEqual: return opLessOrEqual(left, right, expr); + case GreaterOrEqual: return opGreaterOrEqual(left, right, expr); + case Union: return opUnion(left, right, expr); + case In: return opIn(left, right, expr); + case MemberOf: return opMemberOf(context, left, right, expr); + case Contains: return opContains(left, right, expr); + case Or: return opOr(left, right, expr); + case And: return opAnd(left, right, expr); + case Xor: return opXor(left, right, expr); + case Implies: return opImplies(left, right, expr); + case Plus: return opPlus(left, right, expr); + case Times: return opTimes(left, right, expr); + case Minus: return opMinus(left, right, expr); + case Concatenate: return opConcatenate(left, right, expr); + case DivideBy: return opDivideBy(left, right, expr); + case Div: return opDiv(left, right, expr); + case Mod: return opMod(left, right, expr); + case Is: return opIs(left, right, expr); + case As: return opAs(left, right, expr); default: throw new Error("Not Done Yet: "+operation.toCode()); } } - private List opAs(List left, List right) { + private List opAs(List left, List right, ExpressionNode expr) { List result = new ArrayList<>(); if (right.size() != 1) return result; @@ -1455,7 +1456,7 @@ public class FHIRPathEngine { } - private List opIs(List left, List right) { + private List opIs(List left, List right, ExpressionNode expr) { List result = new ArrayList(); if (left.size() != 1 || right.size() != 1) result.add(new BooleanType(false).noExtensions()); @@ -1472,7 +1473,7 @@ public class FHIRPathEngine { } - private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right) { + private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { switch (operation) { case Equals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -1518,7 +1519,7 @@ public class FHIRPathEngine { if (right.hasType(worker, "Quantity")) { result.addType(left.getType()); } else { - throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType())); + throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType()), expr.getStart(), expr.toString()); } } return result; @@ -1579,7 +1580,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquals(List left, List right) { + private List opNotEquals(List left, List right, ExpressionNode expr) { if (!legacyMode && (left.size() == 0 || right.size() == 0)) return new ArrayList(); @@ -1648,9 +1649,9 @@ public class FHIRPathEngine { } - private boolean doEquivalent(Base left, Base right) throws PathEngineException { + private boolean doEquivalent(Base left, Base right, ExpressionNode expr) throws PathEngineException { if (left instanceof Quantity && right instanceof Quantity) - return qtyEquivalent((Quantity) left, (Quantity) right); + return qtyEquivalent((Quantity) left, (Quantity) right, expr); if (left.hasType("integer") && right.hasType("integer")) return doEquals(left, right); if (left.hasType("boolean") && right.hasType("boolean")) @@ -1662,7 +1663,7 @@ public class FHIRPathEngine { if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING)) return Utilities.equivalent(convertToString(left), convertToString(right)); - throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType())); + throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType()), expr.getStart(), expr.toString()); } private boolean qtyEqual(Quantity left, Quantity right) { @@ -1703,19 +1704,19 @@ public class FHIRPathEngine { } - private boolean qtyEquivalent(Quantity left, Quantity right) throws PathEngineException { + private boolean qtyEquivalent(Quantity left, Quantity right, ExpressionNode expr) throws PathEngineException { if (worker.getUcumService() != null) { DecimalType dl = qtyToCanonical(left); DecimalType dr = qtyToCanonical(right); if (dl != null && dr != null) - return doEquivalent(dl, dr); + return doEquivalent(dl, dr, expr); } return left.equals(right); } - private List opEquivalent(List left, List right) throws PathEngineException { + private List opEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) return makeBoolean(false); @@ -1723,7 +1724,7 @@ public class FHIRPathEngine { for (int i = 0; i < left.size(); i++) { boolean found = false; for (int j = 0; j < right.size(); j++) { - if (doEquivalent(left.get(i), right.get(j))) { + if (doEquivalent(left.get(i), right.get(j), expr)) { found = true; break; } @@ -1736,7 +1737,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquivalent(List left, List right) throws PathEngineException { + private List opNotEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) return makeBoolean(true); @@ -1744,7 +1745,7 @@ public class FHIRPathEngine { for (int i = 0; i < left.size(); i++) { boolean found = false; for (int j = 0; j < right.size(); j++) { - if (doEquivalent(left.get(i), right.get(j))) { + if (doEquivalent(left.get(i), right.get(j), expr)) { found = true; break; } @@ -1759,7 +1760,7 @@ public class FHIRPathEngine { private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"}; - private List opLessThan(List left, List right) throws FHIRException { + private List opLessThan(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); @@ -1775,12 +1776,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("code"); List rUnit = right.get(0).listChildrenByName("code"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1789,14 +1790,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opLessThan(dl, dr); + return opLessThan(dl, dr, expr); } } } return new ArrayList(); } - private List opGreater(List left, List right) throws FHIRException { + private List opGreater(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1811,12 +1812,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1825,14 +1826,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opGreater(dl, dr); + return opGreater(dl, dr, expr); } } } return new ArrayList(); } - private List opLessOrEqual(List left, List right) throws FHIRException { + private List opLessOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1847,14 +1848,14 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnits = left.get(0).listChildrenByName("unit"); String lunit = lUnits.size() == 1 ? lUnits.get(0).primitiveValue() : null; List rUnits = right.get(0).listChildrenByName("unit"); String runit = rUnits.size() == 1 ? rUnits.get(0).primitiveValue() : null; if ((lunit == null && runit == null) || lunit.equals(runit)) { - return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1863,14 +1864,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opLessOrEqual(dl, dr); + return opLessOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opGreaterOrEqual(List left, List right) throws FHIRException { + private List opGreaterOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1885,12 +1886,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1899,14 +1900,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opGreaterOrEqual(dl, dr); + return opGreaterOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opMemberOf(ExecutionContext context, List left, List right) throws FHIRException { + private List opMemberOf(ExecutionContext context, List left, List right, ExpressionNode expr) throws FHIRException { boolean ans = false; ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, right.get(0).primitiveValue()) : worker.fetchResource(ValueSet.class, right.get(0).primitiveValue()); if (vs != null) { @@ -1926,7 +1927,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opIn(List left, List right) throws FHIRException { + private List opIn(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0) return new ArrayList(); if (right.size() == 0) @@ -1949,7 +1950,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opContains(List left, List right) { + private List opContains(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) return new ArrayList(); boolean ans = true; @@ -1970,17 +1971,17 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opPlus(List left, List right) throws PathEngineException { + private List opPlus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing +: left operand has more than one value"); + throw new PathEngineException("Error performing +: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing +: right operand has more than one value"); + throw new PathEngineException("Error performing +: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) - throw new PathEngineException(String.format("Error performing +: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -1992,13 +1993,13 @@ public class FHIRPathEngine { else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) result.add(new DecimalType(new BigDecimal(l.primitiveValue()).add(new BigDecimal(r.primitiveValue())))); else if (l.isDateTime() && r.hasType("Quantity")) - result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false)); + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false, expr)); else - throw new PathEngineException(String.format("Error performing +: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate) { + private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate, ExpressionNode expr) { BaseDateTimeType result = (BaseDateTimeType) d.copy(); int value = negate ? 0 - q.getValue().intValue() : q.getValue().intValue(); @@ -2008,13 +2009,13 @@ public class FHIRPathEngine { result.add(Calendar.YEAR, value); break; case "a": - throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode())); + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), expr.getStart(), expr.toString()); case "months": case "month": result.add(Calendar.MONTH, value); break; case "mo": - throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode())); + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), expr.getStart(), expr.toString()); case "weeks": case "week": case "wk": @@ -2046,23 +2047,23 @@ public class FHIRPathEngine { result.add(Calendar.MILLISECOND, value); break; default: - throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode())); + throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode()), expr.getStart(), expr.toString()); } return result; } - private List opTimes(List left, List right) throws PathEngineException { + private List opTimes(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing *: left operand has more than one value"); + throw new PathEngineException("Error performing *: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing *: right operand has more than one value"); + throw new PathEngineException("Error performing *: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing *: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing *: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2080,23 +2081,23 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else - throw new PathEngineException(String.format("Error performing *: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing *: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opConcatenate(List left, List right) throws PathEngineException { + private List opConcatenate(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() > 1) - throw new PathEngineException("Error performing &: left operand has more than one value"); + throw new PathEngineException("Error performing &: left operand has more than one value", expr.getStart(), expr.toString()); if (left.size() > 0 && !left.get(0).hasType(FHIR_TYPES_STRING)) - throw new PathEngineException(String.format("Error performing &: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing &: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing &: right operand has more than one value"); + throw new PathEngineException("Error performing &: right operand has more than one value", expr.getStart(), expr.toString()); if (right.size() > 0 && !right.get(0).hasType(FHIR_TYPES_STRING)) - throw new PathEngineException(String.format("Error performing &: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing &: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); String l = left.size() == 0 ? "" : left.get(0).primitiveValue(); @@ -2105,7 +2106,7 @@ public class FHIRPathEngine { return result; } - private List opUnion(List left, List right) { + private List opUnion(List left, List right, ExpressionNode expr) { List result = new ArrayList(); for (Base item : left) { if (!doContains(result, item)) @@ -2128,9 +2129,9 @@ public class FHIRPathEngine { } - private List opAnd(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opAnd(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case False: return makeBoolean(false); case Null: @@ -2152,9 +2153,9 @@ public class FHIRPathEngine { return list.size() == 1 && list.get(0) instanceof BooleanType && ((BooleanType) list.get(0)).booleanValue() == b; } - private List opOr(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opOr(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: return makeBoolean(true); case Null: @@ -2172,9 +2173,9 @@ public class FHIRPathEngine { return makeNull(); } - private List opXor(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opXor(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: switch (r) { @@ -2194,13 +2195,13 @@ public class FHIRPathEngine { return makeNull(); } - private List opImplies(List left, List right) throws PathEngineException { - Equality eq = asBool(left); + private List opImplies(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality eq = asBool(left, expr); if (eq == Equality.False) return makeBoolean(true); else if (right.size() == 0) return makeNull(); - else switch (asBool(right)) { + else switch (asBool(right, expr)) { case False: return eq == Equality.Null ? makeNull() : makeBoolean(false); case Null: return makeNull(); case True: return makeBoolean(true); @@ -2209,17 +2210,17 @@ public class FHIRPathEngine { } - private List opMinus(List left, List right) throws PathEngineException { + private List opMinus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing -: left operand has more than one value"); + throw new PathEngineException("Error performing -: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing -: right operand has more than one value"); + throw new PathEngineException("Error performing -: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) - throw new PathEngineException(String.format("Error performing -: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2230,23 +2231,23 @@ public class FHIRPathEngine { else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) result.add(new DecimalType(new BigDecimal(l.primitiveValue()).subtract(new BigDecimal(r.primitiveValue())))); else if (l.isDateTime() && r.hasType("Quantity")) - result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true)); + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true, expr)); else - throw new PathEngineException(String.format("Error performing -: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opDivideBy(List left, List right) throws PathEngineException { + private List opDivideBy(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing /: left operand has more than one value"); + throw new PathEngineException("Error performing /: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) throw new PathEngineException("Error performing /: right operand has more than one value"); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing /: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing /: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2269,24 +2270,24 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else - throw new PathEngineException(String.format("Error performing /: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing /: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opDiv(List left, List right) throws PathEngineException { + private List opDiv(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing div: left operand has more than one value"); + throw new PathEngineException("Error performing div: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing div: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing div: right operand has more than one value"); + throw new PathEngineException("Error performing div: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing div: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2305,21 +2306,21 @@ public class FHIRPathEngine { } } else - throw new PathEngineException(String.format("Error performing div: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opMod(List left, List right) throws PathEngineException { + private List opMod(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing mod: left operand has more than one value"); + throw new PathEngineException("Error performing mod: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing mod: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing mod: right operand has more than one value"); + throw new PathEngineException("Error performing mod: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing mod: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2338,12 +2339,12 @@ public class FHIRPathEngine { } } else - throw new PathEngineException(String.format("Error performing mod: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { if (constant instanceof BooleanType) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); else if (constant instanceof IntegerType) @@ -2353,12 +2354,12 @@ 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()); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); else return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); @@ -2372,11 +2373,11 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); else if (s.equals("%resource")) { if (context.resource == null) - throw new PathEngineException("%resource cannot be used in this context"); + throw new PathEngineException("%resource cannot be used in this context", expr.getStart(), expr.toString()); return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%rootResource")) { if (context.resource == null) - throw new PathEngineException("%rootResource cannot be used in this context"); + throw new PathEngineException("%rootResource cannot be used in this context", expr.getStart(), expr.toString()); return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%context")) { return context.context; @@ -2391,7 +2392,7 @@ public class FHIRPathEngine { else if (s.startsWith("%`ext-")) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); else if (hostServices == null) - throw new PathEngineException("Unknown fixed constant type for '"+s+"'"); + throw new PathEngineException("Unknown fixed constant type for '"+s+"'", expr.getStart(), expr.toString()); else return hostServices.resolveConstantType(context.appInfo, s); } @@ -2422,9 +2423,9 @@ public class FHIRPathEngine { return result; } - private TypeDetails executeContextType(ExecutionTypeContext context, String name) throws PathEngineException, DefinitionException { + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { if (hostServices == null) - throw new PathEngineException("Unable to resolve context reference since no host services are provided"); + throw new PathEngineException("Unable to resolve context reference since no host services are provided", expr.getStart(), expr.toString()); return hostServices.resolveConstantType(context.appInfo, name); } @@ -2432,7 +2433,7 @@ public class FHIRPathEngine { if (atEntry && Character.isUpperCase(exp.getName().charAt(0)) && hashTail(type).equals(exp.getName())) // special case for start up return new TypeDetails(CollectionStatus.SINGLETON, type); TypeDetails result = new TypeDetails(null); - getChildTypesByName(type, exp.getName(), result); + getChildTypesByName(type, exp.getName(), result, exp); return result; } @@ -2462,11 +2463,11 @@ public class FHIRPathEngine { case Exists : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case SubsetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SupersetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case IsDistinct : @@ -2486,16 +2487,16 @@ public class FHIRPathEngine { case Aggregate : return anything(focus.getCollectionStatus()); case Item : { - checkOrdered(focus, "item"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "item", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case As : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case OfType : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case Type : { @@ -2513,31 +2514,31 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_ClassInfo); } case Is : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Single : return focus.toSingleton(); case First : { - checkOrdered(focus, "first"); + checkOrdered(focus, "first", exp); return focus.toSingleton(); } case Last : { - checkOrdered(focus, "last"); + checkOrdered(focus, "last", exp); return focus.toSingleton(); } case Tail : { - checkOrdered(focus, "tail"); + checkOrdered(focus, "tail", exp); return focus; } case Skip : { - checkOrdered(focus, "skip"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "skip", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Take : { - checkOrdered(focus, "take"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "take", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Union : { @@ -2560,76 +2561,76 @@ public class FHIRPathEngine { return types; } case Lower : { - checkContextString(focus, "lower"); + checkContextString(focus, "lower", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Upper : { - checkContextString(focus, "upper"); + checkContextString(focus, "upper", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToChars : { - checkContextString(focus, "toChars"); + checkContextString(focus, "toChars", exp); return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); } case IndexOf : { - checkContextString(focus, "indexOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "indexOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Substring : { - checkContextString(focus, "subString"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkContextString(focus, "subString", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case StartsWith : { - checkContextString(focus, "startsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "startsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case EndsWith : { - checkContextString(focus, "endsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "endsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Matches : { - checkContextString(focus, "matches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "matches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ReplaceMatches : { - checkContextString(focus, "replaceMatches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replaceMatches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Contains : { - checkContextString(focus, "contains"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "contains", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Replace : { - checkContextString(focus, "replace"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replace", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Length : { - checkContextPrimitive(focus, "length", false); + checkContextPrimitive(focus, "length", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Children : - return childTypes(focus, "*"); + return childTypes(focus, "*", exp); case Descendants : - return childTypes(focus, "**"); + return childTypes(focus, "**", exp); case MemberOf : { - checkContextCoded(focus, "memberOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextCoded(focus, "memberOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Trace : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Check : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Today : @@ -2637,11 +2638,11 @@ public class FHIRPathEngine { case Now : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); case Resolve : { - checkContextReference(focus, "resolve"); + checkContextReference(focus, "resolve", exp); return new TypeDetails(CollectionStatus.SINGLETON, "DomainResource"); } case Extension : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, "Extension"); } case AnyTrue: @@ -2657,42 +2658,42 @@ public class FHIRPathEngine { case HtmlChecks : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Alias : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return anything(CollectionStatus.SINGLETON); case AliasAs : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; case ToInteger : { - checkContextPrimitive(focus, "toInteger", true); + checkContextPrimitive(focus, "toInteger", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case ToDecimal : { - checkContextPrimitive(focus, "toDecimal", true); + checkContextPrimitive(focus, "toDecimal", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case ToString : { - checkContextPrimitive(focus, "toString", true); + checkContextPrimitive(focus, "toString", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToQuantity : { - checkContextPrimitive(focus, "toQuantity", true); + checkContextPrimitive(focus, "toQuantity", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } case ToBoolean : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ToDateTime : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); } case ToTime : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); } case ConvertsToString : case ConvertsToQuantity :{ - checkContextPrimitive(focus, exp.getFunction().toCode(), true); + checkContextPrimitive(focus, exp.getFunction().toCode(), true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConvertsToInteger : @@ -2700,11 +2701,11 @@ public class FHIRPathEngine { case ConvertsToDateTime : case ConvertsToTime : case ConvertsToBoolean : { - checkContextPrimitive(focus, exp.getFunction().toCode(), false); + checkContextPrimitive(focus, exp.getFunction().toCode(), false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConformsTo: { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Custom : { @@ -2717,7 +2718,7 @@ public class FHIRPathEngine { } - private void checkParamTypes(String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { + private void checkParamTypes(ExpressionNode expr, String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { int i = 0; for (TypeDetails pt : typeSet) { if (i == paramTypes.size()) @@ -2726,47 +2727,47 @@ public class FHIRPathEngine { i++; for (String a : actual.getTypes()) { if (!pt.hasType(worker, a)) - throw new PathEngineException("The parameter type '"+a+"' is not legal for "+funcName+" parameter "+Integer.toString(i)+". expecting "+pt.toString()); + throw new PathEngineException("The parameter type '"+a+"' is not legal for "+funcName+" parameter "+Integer.toString(i)+". expecting "+pt.toString(), expr.getStart(), expr.toString()); } } } - private void checkOrdered(TypeDetails focus, String name) throws PathEngineException { + private void checkOrdered(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (focus.getCollectionStatus() == CollectionStatus.UNORDERED) - throw new PathEngineException("The function '"+name+"'() can only be used on ordered collections"); + throw new PathEngineException("The function '"+name+"'() can only be used on ordered collections", expr.getStart(), expr.toString()); } - private void checkContextReference(TypeDetails focus, String name) throws PathEngineException { + 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")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, canonical, Reference"); + throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, canonical, Reference", expr.getStart(), expr.toString()); } - private void checkContextCoded(TypeDetails focus, String name) throws PathEngineException { + private void checkContextCoded(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Coding") && !focus.hasType(worker, "CodeableConcept")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, code, uri, Coding, CodeableConcept"); + throw new PathEngineException("The function '"+name+"'() can only be used on string, code, uri, Coding, CodeableConcept", expr.getStart(), expr.toString()); } - private void checkContextString(TypeDetails focus, String name) throws PathEngineException { + private void checkContextString(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, code, id, but found "+focus.describe()); + throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, code, id, but found "+focus.describe(), expr.getStart(), expr.toString()); } - private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty) throws PathEngineException { + private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { if (canQty) { if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) - throw new PathEngineException("The function '"+name+"'() can only be used on a Quantity or on "+primitiveTypes.toString()); + throw new PathEngineException("The function '"+name+"'() can only be used on a Quantity or on "+primitiveTypes.toString(), expr.getStart(), expr.toString()); } else if (!focus.hasType(primitiveTypes)) - throw new PathEngineException("The function '"+name+"'() can only be used on "+primitiveTypes.toString()); + throw new PathEngineException("The function '"+name+"'() can only be used on "+primitiveTypes.toString(), expr.getStart(), expr.toString()); } - private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException { + private TypeDetails childTypes(TypeDetails focus, String mask, ExpressionNode expr) throws PathEngineException, DefinitionException { TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); for (String f : focus.getTypes()) - getChildTypesByName(f, mask, result); + getChildTypesByName(f, mask, result, expr); return result; } @@ -2895,7 +2896,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (eq != Equality.True) { all = false; break; @@ -2990,15 +2991,15 @@ public class FHIRPathEngine { result.add(new StringType(n)); } else { - throw new PathEngineException(String.format("funcReplace() : checking for 2 arguments (pattern, substitution) but found %d items", exp.getParameters().size())); + throw new PathEngineException(String.format("funcReplace() : checking for 2 arguments (pattern, substitution) but found %d items", exp.getParameters().size()), exp.getStart(), exp.toString()); } } else { - throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found empty item")); + throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found empty item"), exp.getStart(), exp.toString()); } } else { - throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found %d items", focus.size())); + throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found %d items", focus.size()), exp.getStart(), exp.toString()); } return result; } @@ -3115,7 +3116,7 @@ public class FHIRPathEngine { private List funcIif(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List n1 = execute(context, focus, exp.getParameters().get(0), true); - Equality v = asBool(n1); + Equality v = asBool(n1, exp); if (v == Equality.True) return execute(context, focus, exp.getParameters().get(1), true); @@ -3187,7 +3188,7 @@ public class FHIRPathEngine { private List funcSingle(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { if (focus.size() == 1) return focus; - throw new PathEngineException(String.format("Single() : checking for 1 item but found %d items", focus.size())); + throw new PathEngineException(String.format("Single() : checking for 1 item but found %d items", focus.size()), exp.getStart(), exp.toString()); } @@ -3199,10 +3200,10 @@ public class FHIRPathEngine { ExpressionNode texp = exp.getParameters().get(0); if (texp.getKind() != Kind.Name) - throw new PathEngineException("Unsupported Expression type for Parameter on Is"); + throw new PathEngineException("Unsupported Expression type for Parameter on Is", exp.getStart(), exp.toString()); if (texp.getInner() != null) { if (texp.getInner().getKind() != Kind.Name) - throw new PathEngineException("Unsupported Expression type for Parameter on Is"); + throw new PathEngineException("Unsupported Expression type for Parameter on Is", exp.getStart(), exp.toString()); ns = texp.getName(); n = texp.getInner().getName(); } else if (Utilities.existsInList(texp.getName(), "Boolean", "Integer", "Decimal", "String", "DateTime", "Time", "SimpleTypeInfo", "ClassInfo")) { @@ -3447,7 +3448,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.False) { all = false; break; @@ -3477,7 +3478,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.False) { any = true; break; @@ -3507,7 +3508,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.True) { all = false; break; @@ -3537,7 +3538,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.True) { any = true; break; @@ -3961,7 +3962,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) result.add(item); } @@ -3996,7 +3997,7 @@ public class FHIRPathEngine { private List funcNot(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { List result = new ArrayList(); - Equality v = asBool(focus); + Equality v = asBool(focus, exp); if (v != Equality.Null) result.add(new BooleanType(v != Equality.True)); return result; @@ -4019,9 +4020,9 @@ public class FHIRPathEngine { } - private void getChildTypesByName(String type, String name, TypeDetails result) throws PathEngineException, DefinitionException { + private void getChildTypesByName(String type, String name, TypeDetails result, ExpressionNode expr) throws PathEngineException, DefinitionException { if (Utilities.noString(type)) - throw new PathEngineException("No type provided in BuildToolPathEvaluator.getChildTypesByName"); + throw new PathEngineException("No type provided in BuildToolPathEvaluator.getChildTypesByName", expr.getStart(), expr.toString()); if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml")) return; if (type.startsWith(Constants.NS_SYSTEM_TYPE)) @@ -4045,7 +4046,7 @@ public class FHIRPathEngine { List sdl = new ArrayList(); ElementDefinitionMatch m = null; if (type.contains("#")) - m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false); + m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false, expr); if (m != null && hasDataType(m.definition)) { if (m.fixedType != null) { @@ -4084,11 +4085,11 @@ public class FHIRPathEngine { if (t.getCode().equals("Resource")) { for (String rn : worker.getResourceNames()) { if (!result.hasType(worker, rn)) { - getChildTypesByName(result.addType(rn), "**", result); + getChildTypesByName(result.addType(rn), "**", result, expr); } } } else if (!result.hasType(worker, tn)) { - getChildTypesByName(result.addType(tn), "**", result); + getChildTypesByName(result.addType(tn), "**", result, expr); } } } @@ -4111,7 +4112,7 @@ public class FHIRPathEngine { } else { path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."+name; - ElementDefinitionMatch ed = getElementDefinition(sdi, path, false); + ElementDefinitionMatch ed = getElementDefinition(sdi, path, false, expr); if (ed != null) { if (!Utilities.noString(ed.getFixedType())) result.addType(ed.getFixedType()); @@ -4160,7 +4161,7 @@ public class FHIRPathEngine { } - private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName) throws PathEngineException { + private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException { for (ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(path)) { if (ed.hasContentReference()) { @@ -4180,15 +4181,15 @@ public class FHIRPathEngine { if (ed.getPath().contains(".") && path.startsWith(ed.getPath()+".") && (ed.getType().size() > 0) && !isAbstractType(ed.getType())) { // now we walk into the type. if (ed.getType().size() > 1) // if there's more than one type, the test above would fail this - throw new PathEngineException("Internal typing issue...."); + throw new PathEngineException("Internal typing issue....", expr.getStart(), expr.toString()); StructureDefinition nsd = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getType().get(0).getCode(), worker.getOverrideVersionNs())); if (nsd == null) - throw new PathEngineException("Unknown type "+ed.getType().get(0).getCode()); - return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName); + throw new PathEngineException("Unknown type "+ed.getType().get(0).getCode(), expr.getStart(), expr.toString()); + return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName, expr); } if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) { ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference()); - return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr); } } return null; @@ -4363,13 +4364,13 @@ public class FHIRPathEngine { return path.substring(path.lastIndexOf(".") + 1); } - private Equality asBool(List items) throws PathEngineException { + private Equality asBool(List items, ExpressionNode expr) throws PathEngineException { if (items.size() == 0) return Equality.Null; else if (items.size() == 1) return asBool(items.get(0)); else - throw new PathEngineException("Unable to evaluate as a boolean: "+convertToString(items)); + throw new PathEngineException("Unable to evaluate as a boolean: "+convertToString(items), expr.getStart(), expr.toString()); } private Equality asBoolFromInt(String s) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java index 3ef89fd7b..253a39b65 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java @@ -34,6 +34,7 @@ package org.hl7.fhir.r5.model; import java.util.ArrayList; import java.util.List; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; public class ExpressionNode { @@ -41,31 +42,7 @@ public class ExpressionNode { public enum Kind { Name, Function, Constant, Group, Unary } - public static class SourceLocation { - private int line; - private int column; - public SourceLocation(int line, int column) { - super(); - this.line = line; - this.column = column; - } - public int getLine() { - return line; - } - public int getColumn() { - return column; - } - public void setLine(int line) { - this.line = line; - } - public void setColumn(int column) { - this.column = column; - } - public String toString() { - return Integer.toString(line)+", "+Integer.toString(column); - } - } public enum Function { Custom, diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java index e08acbefb..4400bcdc8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java @@ -34,7 +34,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.model.ExpressionNode; -import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; // shared lexer for concrete syntaxes 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 6f6e4de68..334b8648f 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 @@ -39,7 +39,6 @@ import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.model.ExpressionNode.Function; import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; -import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation; import org.hl7.fhir.r5.model.Property.PropertyMatcher; import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.Property; @@ -59,6 +58,7 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -510,11 +510,11 @@ public class FHIRPathEngine { } StructureDefinition sd = worker.fetchResource(StructureDefinition.class, ctxt); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, context); } - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); } if (ed.fixedType != null) { types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); @@ -531,12 +531,16 @@ public class FHIRPathEngine { return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, true); } - private FHIRException makeException(String constName, Object... args) { + private FHIRException makeException(ExpressionNode holder, String constName, Object... args) { String fmt = worker.formatMessage(constName, args); if (location != null) { fmt = fmt + " "+worker.formatMessage(I18nConstants.FHIRPATH_LOCATION, location); } - return new PathEngineException(fmt); + if (holder != null) { + return new PathEngineException(fmt, holder.getStart(), holder.toString()); + } else { + return new PathEngineException(fmt); + } } public TypeDetails check(Object appContext, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException { @@ -545,9 +549,9 @@ public class FHIRPathEngine { if (!context.contains(".")) { types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); } else { - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); } if (ed.fixedType != null) { types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); @@ -908,7 +912,7 @@ public class FHIRPathEngine { aliases = new HashMap(aliases); // clone it, since it's going to change } if (focus.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_ALIAS_COLLECTION); + throw makeException(null, I18nConstants.FHIRPATH_ALIAS_COLLECTION); } aliases.put(name, focus.size() == 0 ? null : focus.get(0)); } @@ -957,6 +961,7 @@ public class FHIRPathEngine { wrapper = new ExpressionNode(lexer.nextId()); wrapper.setKind(Kind.Unary); wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.take())); + wrapper.setStart(lexer.getCurrentLocation()); wrapper.setProximal(proximal); } @@ -968,6 +973,7 @@ public class FHIRPathEngine { wrapper.setKind(Kind.Unary); wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.getCurrent().substring(0, 1))); wrapper.setProximal(proximal); + wrapper.setStart(lexer.getCurrentLocation()); lexer.setCurrent(lexer.getCurrent().substring(1)); } result.setConstant(processConstant(lexer)); @@ -1355,7 +1361,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - Base b = resolveConstant(context, exp.getConstant(), false); + Base b = resolveConstant(context, exp.getConstant(), false, exp); if (b != null) { work.add(b); } @@ -1373,16 +1379,16 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { - List work2 = preOperate(work, last.getOperation()); + List work2 = preOperate(work, last.getOperation(), exp); if (work2 != null) { work = work2; } else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { work2 = executeTypeName(context, focus, next, false); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); } else { work2 = execute(context, focus, next, true); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); // System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString()); } last = next; @@ -1404,7 +1410,7 @@ public class FHIRPathEngine { } - private List preOperate(List left, Operation operation) throws PathEngineException { + private List preOperate(List left, Operation operation, ExpressionNode expr) throws PathEngineException { if (left.size() == 0) { return null; } @@ -1414,7 +1420,7 @@ public class FHIRPathEngine { case Or: return isBoolean(left, true) ? makeBoolean(true) : null; case Implies: - Equality v = asBool(left); + Equality v = asBool(left, expr); return v == Equality.False ? makeBoolean(true) : null; default: return null; @@ -1445,13 +1451,13 @@ public class FHIRPathEngine { } else if (atEntry && exp.getName().equals("$total")) { result.update(anything(CollectionStatus.UNORDERED)); } else if (atEntry && focus == null) { - result.update(executeContextType(context, exp.getName())); + result.update(executeContextType(context, exp.getName(), exp)); } else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); } if (result.hasNoTypes()) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_NAME, exp.getName(), focus.describe()); + throw makeException(exp, I18nConstants.FHIRPATH_UNKNOWN_NAME, exp.getName(), focus.describe()); } } break; @@ -1464,7 +1470,7 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Quantity); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant())); + result.update(resolveConstantType(context, exp.getConstant(), exp)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1485,7 +1491,7 @@ public class FHIRPathEngine { } else { work = executeType(context, focus, next, atEntry); } - result = operateTypes(result, last.getOperation(), work); + result = operateTypes(result, last.getOperation(), work, last); last = next; next = next.getOpNext(); } @@ -1494,21 +1500,21 @@ public class FHIRPathEngine { return result; } - private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (!(constant instanceof FHIRConstant)) { return constant; } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext); + return resolveConstant(context, c.getValue(), beforeContext, expr); } else if (c.getValue().startsWith("@")) { - return processDateConstant(context.appInfo, c.getValue().substring(1)); + return processDateConstant(context.appInfo, c.getValue().substring(1), expr); } else { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, c.getValue()); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, c.getValue()); } } - private Base processDateConstant(Object appInfo, String value) throws PathEngineException { + private Base processDateConstant(Object appInfo, String value, ExpressionNode expr) throws PathEngineException { String date = null; String time = null; String tz = null; @@ -1556,7 +1562,7 @@ public class FHIRPathEngine { if (date == null) { if (tz != null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value); } else { TimeType tt = new TimeType(time); tt.setPrecision(temp); @@ -1572,7 +1578,7 @@ public class FHIRPathEngine { } - private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (s.equals("%sct")) { return new StringType("http://snomed.info/sct").noExtensions(); } else if (s.equals("%loinc")) { @@ -1581,12 +1587,12 @@ public class FHIRPathEngine { return new StringType("http://unitsofmeasure.org").noExtensions(); } else if (s.equals("%resource")) { if (context.focusResource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); } return context.focusResource; } else if (s.equals("%rootResource")) { if (context.rootResource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); } return context.rootResource; } else if (s.equals("%context")) { @@ -1600,7 +1606,7 @@ public class FHIRPathEngine { } else if (s.startsWith("%`ext-")) { return new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions(); } else if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); } @@ -1661,39 +1667,39 @@ public class FHIRPathEngine { } - private List operate(ExecutionContext context, List left, Operation operation, List right) throws FHIRException { + private List operate(ExecutionContext context, List left, Operation operation, List right, ExpressionNode holder) throws FHIRException { switch (operation) { - case Equals: return opEquals(left, right); - case Equivalent: return opEquivalent(left, right); - case NotEquals: return opNotEquals(left, right); - case NotEquivalent: return opNotEquivalent(left, right); - case LessThan: return opLessThan(left, right); - case Greater: return opGreater(left, right); - case LessOrEqual: return opLessOrEqual(left, right); - case GreaterOrEqual: return opGreaterOrEqual(left, right); - case Union: return opUnion(left, right); - case In: return opIn(left, right); - case MemberOf: return opMemberOf(context, left, right); - case Contains: return opContains(left, right); - case Or: return opOr(left, right); - case And: return opAnd(left, right); - case Xor: return opXor(left, right); - case Implies: return opImplies(left, right); - case Plus: return opPlus(left, right); - case Times: return opTimes(left, right); - case Minus: return opMinus(left, right); - case Concatenate: return opConcatenate(left, right); - case DivideBy: return opDivideBy(left, right); - case Div: return opDiv(left, right); - case Mod: return opMod(left, right); - case Is: return opIs(left, right); - case As: return opAs(left, right); + case Equals: return opEquals(left, right, holder); + case Equivalent: return opEquivalent(left, right, holder); + case NotEquals: return opNotEquals(left, right, holder); + case NotEquivalent: return opNotEquivalent(left, right, holder); + case LessThan: return opLessThan(left, right, holder); + case Greater: return opGreater(left, right, holder); + case LessOrEqual: return opLessOrEqual(left, right, holder); + case GreaterOrEqual: return opGreaterOrEqual(left, right, holder); + case Union: return opUnion(left, right, holder); + case In: return opIn(left, right, holder); + case MemberOf: return opMemberOf(context, left, right, holder); + case Contains: return opContains(left, right, holder); + case Or: return opOr(left, right, holder); + case And: return opAnd(left, right, holder); + case Xor: return opXor(left, right, holder); + case Implies: return opImplies(left, right, holder); + case Plus: return opPlus(left, right, holder); + case Times: return opTimes(left, right, holder); + case Minus: return opMinus(left, right, holder); + case Concatenate: return opConcatenate(left, right, holder); + case DivideBy: return opDivideBy(left, right, holder); + case Div: return opDiv(left, right, holder); + case Mod: return opMod(left, right, holder); + case Is: return opIs(left, right, holder); + case As: return opAs(left, right, holder); default: throw new Error("Not Done Yet: "+operation.toCode()); } } - private List opAs(List left, List right) { + private List opAs(List left, List right, ExpressionNode expr) { List result = new ArrayList<>(); if (right.size() != 1) { return result; @@ -1709,7 +1715,7 @@ public class FHIRPathEngine { } - private List opIs(List left, List right) { + private List opIs(List left, List right, ExpressionNode expr) { List result = new ArrayList(); if (left.size() == 0 || right.size() == 0) { } else if (left.size() != 1 || right.size() != 1) @@ -1728,7 +1734,7 @@ public class FHIRPathEngine { } - private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right) { + private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { switch (operation) { case Equals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -1776,7 +1782,7 @@ public class FHIRPathEngine { if (right.hasType(worker, "Quantity")) { result.addType(left.getType()); } else { - throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType())); + throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType()), expr.getOpStart(), expr.toString()); } } return result; @@ -1814,7 +1820,7 @@ public class FHIRPathEngine { } - private List opEquals(List left, List right) { + private List opEquals(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -1843,7 +1849,7 @@ public class FHIRPathEngine { } } - private List opNotEquals(List left, List right) { + private List opNotEquals(List left, List right, ExpressionNode expr) { if (!legacyMode && (left.size() == 0 || right.size() == 0)) { return new ArrayList(); } @@ -2074,7 +2080,7 @@ public class FHIRPathEngine { - private List opEquivalent(List left, List right) throws PathEngineException { + private List opEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) { return makeBoolean(false); } @@ -2096,7 +2102,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquivalent(List left, List right) throws PathEngineException { + private List opNotEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) { return makeBoolean(true); } @@ -2120,7 +2126,7 @@ public class FHIRPathEngine { private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"}; - private List opLessThan(List left, List right) throws FHIRException { + private List opLessThan(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); @@ -2146,13 +2152,13 @@ public class FHIRPathEngine { return makeBoolean(i < 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("code"); List rUnit = right.get(0).listChildrenByName("code"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2161,14 +2167,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opLessThan(dl, dr); + return opLessThan(dl, dr, expr); } } } return new ArrayList(); } - private List opGreater(List left, List right) throws FHIRException { + private List opGreater(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -2193,13 +2199,13 @@ public class FHIRPathEngine { return makeBoolean(i > 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2208,14 +2214,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opGreater(dl, dr); + return opGreater(dl, dr, expr); } } } return new ArrayList(); } - private List opLessOrEqual(List left, List right) throws FHIRException { + private List opLessOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2241,7 +2247,7 @@ public class FHIRPathEngine { return makeBoolean(i <= 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnits = left.get(0).listChildrenByName("unit"); @@ -2249,7 +2255,7 @@ public class FHIRPathEngine { List rUnits = right.get(0).listChildrenByName("unit"); String runit = rUnits.size() == 1 ? rUnits.get(0).primitiveValue() : null; if ((lunit == null && runit == null) || lunit.equals(runit)) { - return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2258,14 +2264,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opLessOrEqual(dl, dr); + return opLessOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opGreaterOrEqual(List left, List right) throws FHIRException { + private List opGreaterOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2291,13 +2297,13 @@ public class FHIRPathEngine { return makeBoolean(i >= 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2306,14 +2312,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opGreaterOrEqual(dl, dr); + return opGreaterOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opMemberOf(ExecutionContext context, List left, List right) throws FHIRException { + private List opMemberOf(ExecutionContext context, List left, List right, ExpressionNode expr) throws FHIRException { boolean ans = false; ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, right.get(0).primitiveValue()) : worker.fetchResource(ValueSet.class, right.get(0).primitiveValue()); if (vs != null) { @@ -2338,7 +2344,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opIn(List left, List right) throws FHIRException { + private List opIn(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0) { return new ArrayList(); } @@ -2363,7 +2369,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opContains(List left, List right) { + private List opContains(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2385,21 +2391,21 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opPlus(List left, List right) throws PathEngineException { + private List opPlus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "+"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "+"); } if (!left.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "+", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "+", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "+"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "+"); } if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "+", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "+", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2412,14 +2418,14 @@ public class FHIRPathEngine { } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { result.add(new DecimalType(new BigDecimal(l.primitiveValue()).add(new BigDecimal(r.primitiveValue())))); } else if (l.isDateTime() && r.hasType("Quantity")) { - result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false)); + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false, expr)); } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "+", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "+", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate) { + private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate, ExpressionNode holder) { BaseDateTimeType result = (BaseDateTimeType) d.copy(); int value = negate ? 0 - q.getValue().intValue() : q.getValue().intValue(); @@ -2435,7 +2441,7 @@ public class FHIRPathEngine { result.add(Calendar.MONTH, value); break; case "mo": - throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode())); + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), holder.getOpStart(), holder.toString()); case "weeks": case "week": case "wk": @@ -2472,21 +2478,21 @@ public class FHIRPathEngine { return result; } - private List opTimes(List left, List right) throws PathEngineException { + private List opTimes(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "*"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "*"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "*", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "*", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "*"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "*"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "*", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "*", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2505,27 +2511,27 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opConcatenate(List left, List right) throws PathEngineException { + private List opConcatenate(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "&"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "&"); } if (left.size() > 0 && !left.get(0).hasType(FHIR_TYPES_STRING)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "&", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "&", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "&"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "&"); } if (right.size() > 0 && !right.get(0).hasType(FHIR_TYPES_STRING)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "&", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "&", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2535,7 +2541,7 @@ public class FHIRPathEngine { return result; } - private List opUnion(List left, List right) { + private List opUnion(List left, List right, ExpressionNode expr) { List result = new ArrayList(); for (Base item : left) { if (!doContains(result, item)) { @@ -2561,9 +2567,9 @@ public class FHIRPathEngine { } - private List opAnd(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opAnd(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case False: return makeBoolean(false); case Null: @@ -2586,9 +2592,9 @@ public class FHIRPathEngine { return list.size() == 1 && list.get(0) instanceof BooleanType && ((BooleanType) list.get(0)).booleanValue() == b; } - private List opOr(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opOr(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: return makeBoolean(true); case Null: @@ -2607,9 +2613,9 @@ public class FHIRPathEngine { return makeNull(); } - private List opXor(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opXor(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: switch (r) { @@ -2629,13 +2635,13 @@ public class FHIRPathEngine { return makeNull(); } - private List opImplies(List left, List right) throws PathEngineException { - Equality eq = asBool(left); + private List opImplies(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality eq = asBool(left, expr); if (eq == Equality.False) { return makeBoolean(true); } else if (right.size() == 0) { return makeNull(); - } else switch (asBool(right)) { + } else switch (asBool(right, expr)) { case False: return eq == Equality.Null ? makeNull() : makeBoolean(false); case Null: return makeNull(); case True: return makeBoolean(true); @@ -2644,21 +2650,21 @@ public class FHIRPathEngine { } - private List opMinus(List left, List right) throws PathEngineException { + private List opMinus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-"); } if (!left.get(0).isPrimitive() && !left.get(0).hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-"); } if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2676,28 +2682,28 @@ public class FHIRPathEngine { result.add(qty.copy().setValue(qty.getValue().abs())); } } else if (l.isDateTime() && r.hasType("Quantity")) { - result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true)); + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true, expr)); } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opDivideBy(List left, List right) throws PathEngineException { + private List opDivideBy(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "/"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "/"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "/", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "/", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "/"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "/"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "/", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "/", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2724,26 +2730,26 @@ public class FHIRPathEngine { // just return nothing } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opDiv(List left, List right) throws PathEngineException { + private List opDiv(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "div"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "div"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "div", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "div", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "div"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "div"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "div", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "div", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2765,25 +2771,25 @@ public class FHIRPathEngine { // just return nothing } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opMod(List left, List right) throws PathEngineException { + private List opMod(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "mod"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "mod"); } if (!left.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "mod", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "mod", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "mod"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "mod"); } if (!right.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "mod", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "mod", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2805,13 +2811,13 @@ public class FHIRPathEngine { throw new PathEngineException(e); } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "mod", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "mod", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { if (constant instanceof BooleanType) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } else if (constant instanceof IntegerType) { @@ -2821,13 +2827,13 @@ 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()); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); } else { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); @@ -2842,12 +2848,12 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } else if (s.equals("%resource")) { if (context.resource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); } return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%rootResource")) { if (context.resource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); } return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%context")) { @@ -2863,7 +2869,7 @@ public class FHIRPathEngine { } else if (s.startsWith("%`ext-")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } else if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { return hostServices.resolveConstantType(context.appInfo, s); } @@ -2914,9 +2920,9 @@ public class FHIRPathEngine { } - private TypeDetails executeContextType(ExecutionTypeContext context, String name) throws PathEngineException, DefinitionException { + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); + throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); } return hostServices.resolveConstantType(context.appInfo, name); } @@ -2926,7 +2932,7 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, type); } TypeDetails result = new TypeDetails(null); - getChildTypesByName(type, exp.getName(), result); + getChildTypesByName(type, exp.getName(), result, exp); return result; } @@ -2956,15 +2962,15 @@ public class FHIRPathEngine { case Not : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Exists : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SubsetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SupersetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case IsDistinct : @@ -2984,16 +2990,16 @@ public class FHIRPathEngine { case Aggregate : return anything(focus.getCollectionStatus()); case Item : { - checkOrdered(focus, "item"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "item", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case As : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case OfType : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case Type : { @@ -3012,31 +3018,31 @@ public class FHIRPathEngine { } } case Is : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Single : return focus.toSingleton(); case First : { - checkOrdered(focus, "first"); + checkOrdered(focus, "first", exp); return focus.toSingleton(); } case Last : { - checkOrdered(focus, "last"); + checkOrdered(focus, "last", exp); return focus.toSingleton(); } case Tail : { - checkOrdered(focus, "tail"); + checkOrdered(focus, "tail", exp); return focus; } case Skip : { - checkOrdered(focus, "skip"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "skip", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Take : { - checkOrdered(focus, "take"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "take", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Union : { @@ -3060,76 +3066,76 @@ public class FHIRPathEngine { return types; } case Lower : { - checkContextString(focus, "lower"); + checkContextString(focus, "lower", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Upper : { - checkContextString(focus, "upper"); + checkContextString(focus, "upper", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToChars : { - checkContextString(focus, "toChars"); + checkContextString(focus, "toChars", exp); return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); } case IndexOf : { - checkContextString(focus, "indexOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "indexOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Substring : { - checkContextString(focus, "subString"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkContextString(focus, "subString", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case StartsWith : { - checkContextString(focus, "startsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "startsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case EndsWith : { - checkContextString(focus, "endsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "endsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Matches : { - checkContextString(focus, "matches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "matches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ReplaceMatches : { - checkContextString(focus, "replaceMatches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replaceMatches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Contains : { - checkContextString(focus, "contains"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "contains", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Replace : { - checkContextString(focus, "replace"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replace", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Length : { - checkContextPrimitive(focus, "length", false); + checkContextPrimitive(focus, "length", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Children : - return childTypes(focus, "*"); + return childTypes(focus, "*", exp); case Descendants : - return childTypes(focus, "**"); + return childTypes(focus, "**", exp); case MemberOf : { - checkContextCoded(focus, "memberOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextCoded(focus, "memberOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Trace : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Check : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Today : @@ -3137,11 +3143,11 @@ public class FHIRPathEngine { case Now : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); case Resolve : { - checkContextReference(focus, "resolve"); + checkContextReference(focus, "resolve", exp); return new TypeDetails(CollectionStatus.SINGLETON, "DomainResource"); } case Extension : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, "Extension"); } case AnyTrue: @@ -3159,62 +3165,62 @@ public class FHIRPathEngine { case HtmlChecks2 : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Alias : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return anything(CollectionStatus.SINGLETON); case AliasAs : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; case Encode: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Decode: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Escape: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Unescape: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Trim: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Split: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Join: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case ToInteger : { - checkContextPrimitive(focus, "toInteger", true); + checkContextPrimitive(focus, "toInteger", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case ToDecimal : { - checkContextPrimitive(focus, "toDecimal", true); + checkContextPrimitive(focus, "toDecimal", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case ToString : { - checkContextPrimitive(focus, "toString", true); + checkContextPrimitive(focus, "toString", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToQuantity : { - checkContextPrimitive(focus, "toQuantity", true); + checkContextPrimitive(focus, "toQuantity", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } case ToBoolean : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ToDateTime : { - checkContextPrimitive(focus, "ToDateTime", false); + checkContextPrimitive(focus, "ToDateTime", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); } case ToTime : { - checkContextPrimitive(focus, "ToTime", false); + checkContextPrimitive(focus, "ToTime", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); } case ConvertsToString : case ConvertsToQuantity :{ - checkContextPrimitive(focus, exp.getFunction().toCode(), true); + checkContextPrimitive(focus, exp.getFunction().toCode(), true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConvertsToInteger : @@ -3223,28 +3229,28 @@ public class FHIRPathEngine { case ConvertsToDate : case ConvertsToTime : case ConvertsToBoolean : { - checkContextPrimitive(focus, exp.getFunction().toCode(), false); + checkContextPrimitive(focus, exp.getFunction().toCode(), false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConformsTo: { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Abs : { - checkContextNumerical(focus, "abs"); + checkContextNumerical(focus, "abs", exp); return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); } case Truncate : case Floor : case Ceiling : { - checkContextDecimal(focus, exp.getFunction().toCode()); + checkContextDecimal(focus, exp.getFunction().toCode(), exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Round :{ - checkContextDecimal(focus, "round"); + checkContextDecimal(focus, "round", exp); if (paramTypes.size() > 0) { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); } return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } @@ -3252,17 +3258,17 @@ public class FHIRPathEngine { case Exp : case Ln : case Sqrt : { - checkContextNumerical(focus, exp.getFunction().toCode()); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case Log : { - checkContextNumerical(focus, exp.getFunction().toCode()); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case Power : { - checkContextNumerical(focus, exp.getFunction().toCode()); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); } @@ -3276,7 +3282,7 @@ public class FHIRPathEngine { } - private void checkParamTypes(String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { + private void checkParamTypes(ExpressionNode expr, String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { int i = 0; for (TypeDetails pt : typeSet) { if (i == paramTypes.size()) { @@ -3286,65 +3292,65 @@ public class FHIRPathEngine { i++; for (String a : actual.getTypes()) { if (!pt.hasType(worker, a)) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, funcName, i, a, pt.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, funcName, i, a, pt.toString()); } } } } - private void checkOrdered(TypeDetails focus, String name) throws PathEngineException { + private void checkOrdered(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (focus.getCollectionStatus() == CollectionStatus.UNORDERED) { - throw makeException(I18nConstants.FHIRPATH_ORDERED_ONLY, name); + throw makeException(expr, I18nConstants.FHIRPATH_ORDERED_ONLY, name); } } - private void checkContextReference(TypeDetails focus, String name) throws PathEngineException { + 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")) { - throw makeException(I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); } } - private void checkContextCoded(TypeDetails focus, String name) throws PathEngineException { + private void checkContextCoded(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Coding") && !focus.hasType(worker, "CodeableConcept")) { - throw makeException(I18nConstants.FHIRPATH_CODED_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_CODED_ONLY, name, focus.describe()); } } - private void checkContextString(TypeDetails focus, String name) throws PathEngineException { + private void checkContextString(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) { - throw makeException(I18nConstants.FHIRPATH_STRING_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_STRING_ONLY, name, focus.describe()); } } - private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty) throws PathEngineException { + private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { if (canQty) { if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); } } else if (!focus.hasType(primitiveTypes)) { - throw makeException(I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); } } - private void checkContextNumerical(TypeDetails focus, String name) throws PathEngineException { + private void checkContextNumerical(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); } } - private void checkContextDecimal(TypeDetails focus, String name) throws PathEngineException { + private void checkContextDecimal(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType("decimal") && !focus.hasType("integer")) { - throw makeException(I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); } } - private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException { + private TypeDetails childTypes(TypeDetails focus, String mask, ExpressionNode expr) throws PathEngineException, DefinitionException { TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); for (String f : focus.getTypes()) { - getChildTypesByName(f, mask, result); + getChildTypesByName(f, mask, result, expr); } return result; } @@ -3464,9 +3470,9 @@ public class FHIRPathEngine { } } - private List funcSqrt(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcSqrt(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3478,15 +3484,15 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcAbs(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcAbs(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "abs", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "abs", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3501,15 +3507,15 @@ public class FHIRPathEngine { Quantity qty = (Quantity) base; result.add(qty.copy().setValue(qty.getValue().abs())); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcCeiling(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcCeiling(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ceiling", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "ceiling", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3520,14 +3526,14 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcFloor(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcFloor(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "floor", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "floor", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3539,15 +3545,15 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcExp(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcExp(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3560,15 +3566,15 @@ public class FHIRPathEngine { } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcLn(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcLn(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ln", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "ln", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3580,22 +3586,22 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcLog(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcLog(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "log", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "log", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal"); } Double e = Double.parseDouble(n1.get(0).primitiveValue()); Double d = Double.parseDouble(base.primitiveValue()); @@ -3605,7 +3611,7 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal"); } return result; } @@ -3614,16 +3620,16 @@ public class FHIRPathEngine { return Math.log(logNumber) / Math.log(base); } - private List funcPower(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcPower(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "power", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "power", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal"); } Double e = Double.parseDouble(n1.get(0).primitiveValue()); Double d = Double.parseDouble(base.primitiveValue()); @@ -3633,14 +3639,14 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcTruncate(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcTruncate(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "truncate", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "truncate", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3651,30 +3657,30 @@ public class FHIRPathEngine { } result.add(new IntegerType(s)); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcRound(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcRound(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "round", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "round", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { int i = 0; - if (exp.getParameters().size() == 1) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + if (expr.getParameters().size() == 1) { + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer"); } i = Integer.parseInt(n1.get(0).primitiveValue()); } BigDecimal d = new BigDecimal (base.primitiveValue()); result.add(new DecimalType(d.setScale(i, RoundingMode.HALF_UP))); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal"); } return result; } @@ -3913,7 +3919,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (eq != Equality.True) { all = false; break; @@ -4012,7 +4018,7 @@ public class FHIRPathEngine { } - private List funcReplace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException, PathEngineException { + private List funcReplace(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException, PathEngineException { List result = new ArrayList(); if (focus.size() == 1) { @@ -4020,13 +4026,13 @@ public class FHIRPathEngine { if (Utilities.noString(f)) { result.add(new StringType("")); } else { - String t = convertToString(execute(context, focus, exp.getParameters().get(0), true)); - String r = convertToString(execute(context, focus, exp.getParameters().get(1), true)); + String t = convertToString(execute(context, focus, expr.getParameters().get(0), true)); + String r = convertToString(execute(context, focus, expr.getParameters().get(1), true)); String n = f.replace(t, r); result.add(new StringType(n)); } } else { - throw makeException(I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size()); } return result; } @@ -4119,22 +4125,22 @@ public class FHIRPathEngine { return result; } - private List funcToDateTime(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToDateTime(ExecutionContext context, List focus, ExpressionNode expr) { // List result = new ArrayList(); // result.add(new BooleanType(convertToBoolean(focus))); // return result; - throw makeException(I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); + throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); } - private List funcToTime(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToTime(ExecutionContext context, List focus, ExpressionNode expr) { // List result = new ArrayList(); // result.add(new BooleanType(convertToBoolean(focus))); // return result; - throw makeException(I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); + throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); } - private List funcToDecimal(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToDecimal(ExecutionContext context, List focus, ExpressionNode expr) { String s = convertToString(focus); List result = new ArrayList(); if (Utilities.isDecimal(s, true)) { @@ -4152,7 +4158,7 @@ public class FHIRPathEngine { private List funcIif(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List n1 = execute(context, focus, exp.getParameters().get(0), true); - Equality v = asBool(n1); + Equality v = asBool(n1, exp); if (v == Equality.True) { return execute(context, focus, exp.getParameters().get(1), true); @@ -4227,28 +4233,28 @@ public class FHIRPathEngine { } - private List funcSingle(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { + private List funcSingle(ExecutionContext context, List focus, ExpressionNode expr) throws PathEngineException { if (focus.size() == 1) { return focus; } - throw makeException(I18nConstants.FHIRPATH_NO_COLLECTION, "single", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "single", focus.size()); } - private List funcIs(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { + private List funcIs(ExecutionContext context, List focus, ExpressionNode expr) throws PathEngineException { if (focus.size() == 0 || focus.size() > 1) { return makeNull(); } String ns = null; String n = null; - ExpressionNode texp = exp.getParameters().get(0); + ExpressionNode texp = expr.getParameters().get(0); if (texp.getKind() != Kind.Name) { - throw makeException(I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "0", "is"); + throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "0", "is"); } if (texp.getInner() != null) { if (texp.getInner().getKind() != Kind.Name) { - throw makeException(I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "1", "is"); + throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "1", "is"); } ns = texp.getName(); n = texp.getInner().getName(); @@ -4284,13 +4290,13 @@ public class FHIRPathEngine { } - private List funcAs(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcAs(ExecutionContext context, List focus, ExpressionNode expr) { List result = new ArrayList(); String tn; - if (exp.getParameters().get(0).getInner() != null) { - tn = exp.getParameters().get(0).getName()+"."+exp.getParameters().get(0).getInner().getName(); + if (expr.getParameters().get(0).getInner() != null) { + tn = expr.getParameters().get(0).getName()+"."+expr.getParameters().get(0).getInner().getName(); } else { - tn = "FHIR."+exp.getParameters().get(0).getName(); + tn = "FHIR."+expr.getParameters().get(0).getName(); } for (Base b : focus) { if (tn.startsWith("System.")) { @@ -4436,7 +4442,7 @@ public class FHIRPathEngine { if (exp.getParameters().size() == 1) { pc.clear(); pc.add(f); - Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) { empty = false; } @@ -4531,7 +4537,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.False) { all = false; break; @@ -4565,7 +4571,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.False) { any = true; break; @@ -4599,7 +4605,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.True) { all = false; break; @@ -4632,7 +4638,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.True) { any = true; break; @@ -4673,12 +4679,12 @@ public class FHIRPathEngine { return focus; } - private List funcCheck(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + private List funcCheck(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException { + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (!convertToBoolean(n1)) { - List n2 = execute(context, focus, exp.getParameters().get(1), true); + List n2 = execute(context, focus, expr.getParameters().get(1), true); String name = n2.get(0).primitiveValue(); - throw makeException(I18nConstants.FHIRPATH_CHECK_FAILED, name); + throw makeException(expr, I18nConstants.FHIRPATH_CHECK_FAILED, name); } return focus; } @@ -4940,15 +4946,15 @@ public class FHIRPathEngine { return result; } - private List funcConformsTo(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + private List funcConformsTo(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException { if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_HO_HOST_SERVICES, "conformsTo"); + throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "conformsTo"); } List result = new ArrayList(); if (focus.size() != 1) { result.add(new BooleanType(false).noExtensions()); } else { - String url = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); } return result; @@ -5111,7 +5117,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) { result.add(item); } @@ -5148,7 +5154,7 @@ public class FHIRPathEngine { private List funcNot(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { List result = new ArrayList(); - Equality v = asBool(focus); + Equality v = asBool(focus, exp); if (v != Equality.Null) { result.add(new BooleanType(v != Equality.True)); } @@ -5172,9 +5178,9 @@ public class FHIRPathEngine { } - private void getChildTypesByName(String type, String name, TypeDetails result) throws PathEngineException, DefinitionException { + private void getChildTypesByName(String type, String name, TypeDetails result, ExpressionNode expr) throws PathEngineException, DefinitionException { if (Utilities.noString(type)) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, "", "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, "", "getChildTypesByName"); } if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml")) { return; @@ -5197,24 +5203,24 @@ public class FHIRPathEngine { String tail = ""; StructureDefinition sd = worker.fetchResource(StructureDefinition.class, url); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, url, "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, url, "getChildTypesByName"); } List sdl = new ArrayList(); ElementDefinitionMatch m = null; if (type.contains("#")) - m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false); + m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false, expr); if (m != null && hasDataType(m.definition)) { if (m.fixedType != null) { StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs())); if (dt == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs()), "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs()), "getChildTypesByName"); } sdl.add(dt); } else for (TypeRefComponent t : m.definition.getType()) { StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs())); if (dt == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs()), "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs()), "getChildTypesByName"); } sdl.add(dt); } @@ -5243,11 +5249,11 @@ public class FHIRPathEngine { if (t.getCode().equals("Resource")) { for (String rn : worker.getResourceNames()) { if (!result.hasType(worker, rn)) { - getChildTypesByName(result.addType(rn), "**", result); + getChildTypesByName(result.addType(rn), "**", result, expr); } } } else if (!result.hasType(worker, tn)) { - getChildTypesByName(result.addType(tn), "**", result); + getChildTypesByName(result.addType(tn), "**", result, expr); } } } @@ -5271,7 +5277,7 @@ public class FHIRPathEngine { } else { path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."+name; - ElementDefinitionMatch ed = getElementDefinition(sdi, path, isAllowPolymorphicNames()); + ElementDefinitionMatch ed = getElementDefinition(sdi, path, isAllowPolymorphicNames(), expr); if (ed != null) { if (!Utilities.noString(ed.getFixedType())) result.addType(ed.getFixedType()); @@ -5329,7 +5335,7 @@ public class FHIRPathEngine { } - private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName) throws PathEngineException { + private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException { for (ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(path)) { if (ed.hasContentReference()) { @@ -5356,13 +5362,13 @@ public class FHIRPathEngine { } StructureDefinition nsd = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getType().get(0).getCode(), worker.getOverrideVersionNs())); if (nsd == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ed.getType().get(0).getCode(), "getElementDefinition"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ed.getType().get(0).getCode(), "getElementDefinition"); } - return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName, expr); } if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) { ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference()); - return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr); } } return null; @@ -5430,7 +5436,7 @@ public class FHIRPathEngine { if (element.hasSlicing()) { ElementDefinition slice = pickMandatorySlice(sd, element); if (slice == null) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NAME_ALREADY_SLICED, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NAME_ALREADY_SLICED, element.getId()); } element = slice; } @@ -5443,9 +5449,9 @@ public class FHIRPathEngine { // if that's empty, get the children of the type if (childDefinitions.isEmpty()) { - sd = fetchStructureByType(element); + sd = fetchStructureByType(element, expr); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND, element.getType().get(0).getProfile(), element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND, element.getType().get(0).getProfile(), element.getId()); } childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); } @@ -5459,20 +5465,20 @@ public class FHIRPathEngine { } else if (expr.getKind() == Kind.Function) { if ("resolve".equals(expr.getName())) { if (!element.hasType()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NO_TYPE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NO_TYPE, element.getId()); } if (element.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_MULTIPLE_TYPES, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_MULTIPLE_TYPES, element.getId()); } if (!element.getType().get(0).hasTarget()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NOT_REFERENCE, element.getId(), element.getType().get(0).getCode()+")"); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NOT_REFERENCE, element.getId(), element.getType().get(0).getCode()+")"); } if (element.getType().get(0).getTargetProfile().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET, element.getId()); } sd = worker.fetchResource(StructureDefinition.class, element.getType().get(0).getTargetProfile().get(0).getValue()); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getType().get(0).getTargetProfile(), element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getType().get(0).getTargetProfile(), element.getId()); } focus = sd.getSnapshot().getElementFirstRep(); } else if ("extension".equals(expr.getName())) { @@ -5496,13 +5502,13 @@ public class FHIRPathEngine { } } else if ("ofType".equals(expr.getName())) { if (!element.hasType()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_NONE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_NONE, element.getId()); } if (element.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE, element.getId()); } if (!element.getType().get(0).hasCode()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NO_CODE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NO_CODE, element.getId()); } String atn = element.getType().get(0).getCode(); String stn = expr.getParameters().get(0).getName(); @@ -5511,19 +5517,19 @@ public class FHIRPathEngine { focus = element; } } else { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName()); } } else if (expr.getKind() == Kind.Group) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP); } else if (expr.getKind() == Kind.Constant) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST); } if (focus == null) { if (okToNotResolve) { return null; } else { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND, expr.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND, expr.toString()); } } else if (expr.getInner() == null) { return focus; @@ -5543,15 +5549,15 @@ public class FHIRPathEngine { } - private StructureDefinition fetchStructureByType(ElementDefinition ed) throws DefinitionException { + private StructureDefinition fetchStructureByType(ElementDefinition ed, ExpressionNode expr) throws DefinitionException { if (ed.getType().size() == 0) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NOTYPE, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NOTYPE, ed.getId()); } if (ed.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_TYPES, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_TYPES, ed.getId()); } if (ed.getType().get(0).getProfile().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_PROFILES, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_PROFILES, ed.getId()); } if (ed.getType().get(0).hasProfile()) { return worker.fetchResource(StructureDefinition.class, ed.getType().get(0).getProfile().get(0).getValue()); @@ -5579,7 +5585,7 @@ public class FHIRPathEngine { return path.substring(path.lastIndexOf(".") + 1); } - private Equality asBool(List items) throws PathEngineException { + private Equality asBool(List items, ExpressionNode expr) throws PathEngineException { if (items.size() == 0) { return Equality.Null; } else if (items.size() == 1 && items.get(0).isBooleanPrimitive()) { @@ -5587,7 +5593,7 @@ public class FHIRPathEngine { } else if (items.size() == 1) { return Equality.True; } else { - throw makeException(I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); + throw makeException(expr, I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); } } 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 322027805..22b3e3e21 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 @@ -176,6 +176,7 @@ public class FHIRPathTests { node = fp.parse(expression); Assertions.assertTrue(fail != TestResultType.SYNTAX, String.format("Expected exception didn't occur parsing %s", expression)); } catch (Exception e) { + System.out.println("Parsing Error: "+e.getMessage()); Assertions.assertTrue(fail == TestResultType.SYNTAX, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression)); } @@ -193,6 +194,7 @@ public class FHIRPathTests { } 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; } @@ -203,6 +205,7 @@ public class FHIRPathTests { outcome = fp.evaluate(res, node); Assertions.assertTrue(fail == TestResultType.OK, String.format("Expected exception didn't occur executing %s", expression)); } catch (Exception e) { + System.out.println("Execution Error: "+e.getMessage()); Assertions.assertTrue(fail == TestResultType.EXECUTION, String.format("Unexpected exception executing %s: " + e.getMessage(), expression)); node = null; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java index 1eb33bdeb..b26a5ce16 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java @@ -1,5 +1,8 @@ package org.hl7.fhir.exceptions; +import org.hl7.fhir.utilities.SourceLocation; +import org.hl7.fhir.utilities.Utilities; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -33,20 +36,62 @@ package org.hl7.fhir.exceptions; public class PathEngineException extends FHIRException { + private static final long serialVersionUID = 31969342112856390L; + private SourceLocation location; + private String expression; + public PathEngineException() { super(); } - public PathEngineException(String message, Throwable cause) { - super(message, cause); - } + public PathEngineException(String message, Throwable cause) { + super(message, cause); + } - public PathEngineException(String message) { - super(message); - } + public PathEngineException(String message) { + super(message); + } - public PathEngineException(Throwable cause) { + public PathEngineException(String message, SourceLocation location, String expression, Throwable cause) { + super(message+rep(location, expression), cause); + } + + public PathEngineException(String message, SourceLocation location, String expression) { + super(message+rep(location, expression)); + } + + private static String rep(SourceLocation loc, String expr) { + if (loc != null) { + if (loc.getLine() == 1) { + return " (@char "+loc.getColumn()+")"; + } else { + return " (@line "+loc.getLine()+" char "+loc.getColumn()+")"; + } + } else if (Utilities.noString(expr)) { // can happen in some contexts... + return " (@~"+expr+")"; + } else { + return ""; + } + } + + public PathEngineException(Throwable cause) { super(cause); } + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } + + public SourceLocation getLocation() { + return location; + } + + public void setLocation(SourceLocation location) { + this.location = location; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java new file mode 100644 index 000000000..5ed864765 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java @@ -0,0 +1,27 @@ +package org.hl7.fhir.utilities; + +public class SourceLocation { + private int line; + private int column; + public SourceLocation(int line, int column) { + super(); + this.line = line; + this.column = column; + } + public int getLine() { + return line; + } + public int getColumn() { + return column; + } + public void setLine(int line) { + this.line = line; + } + public void setColumn(int column) { + this.column = column; + } + + public String toString() { + return Integer.toString(line)+", "+Integer.toString(column); + } +}