diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/fhirpath/FHIRPathEngine.java
index 3ca6de5b9..80920c0fa 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/fhirpath/FHIRPathEngine.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/fhirpath/FHIRPathEngine.java
@@ -4817,7 +4817,15 @@ public class FHIRPathEngine {
private List funcToString(ExecutionContext context, List focus, ExpressionNode exp) {
List result = new ArrayList();
- result.add(new StringType(convertToString(focus)).noExtensions());
+ for (Base item : focus) {
+ String value = convertToString(item);
+ if (value != null)
+ result.add(new StringType(value).noExtensions());
+ }
+
+ if (result.size() > 1) {
+ throw makeException(exp, I18nConstants.FHIRPATH_NO_COLLECTION, "toString", result.size());
+ }
return result;
}
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/fhirpath/FHIRPathEngine.java
index 60d32662d..eaf53f0d1 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/fhirpath/FHIRPathEngine.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/fhirpath/FHIRPathEngine.java
@@ -4820,7 +4820,15 @@ public class FHIRPathEngine {
private List funcToString(ExecutionContext context, List focus, ExpressionNode exp) {
List result = new ArrayList();
- result.add(new StringType(convertToString(focus)).noExtensions());
+ for (Base item : focus) {
+ String value = convertToString(item);
+ if (value != null)
+ result.add(new StringType(value).noExtensions());
+ }
+
+ if (result.size() > 1) {
+ throw makeException(exp, I18nConstants.FHIRPATH_NO_COLLECTION, "toString", result.size());
+ }
return result;
}
diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java
index 7a92e17b8..61b40c091 100644
--- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java
+++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/FHIRPathTests.java
@@ -336,4 +336,23 @@ public class FHIRPathTests {
assertEquals(1, results.size());
assertEquals("123", results.get(0).toString());
}
+
+ @Test
+ public void testEvaluate_ToStringOnDateValue() {
+ Patient input = new Patient();
+ var dtv = new DateType("2024");
+ input.setBirthDateElement(dtv);
+ List results = fp.evaluate(input, "Patient.birthDate.toString()");
+ assertEquals(1, results.size());
+ assertEquals("2024", results.get(0).toString());
+ }
+
+ @Test
+ public void testEvaluate_ToStringOnExtensionOnlyValue() {
+ Patient input = new Patient();
+ var dtv = new DateType();
+ input.setBirthDateElement(dtv);
+ List results = fp.evaluate(input, "Patient.birthDate.toString()");
+ assertEquals(0, results.size());
+ }
}
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java
index 126ee57cc..c33917860 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/fhirpath/FHIRPathEngine.java
@@ -4929,7 +4929,15 @@ public class FHIRPathEngine {
private List funcToString(ExecutionContext context, List focus, ExpressionNode exp) {
List result = new ArrayList();
- result.add(new StringType(convertToString(focus)).noExtensions());
+ for (Base item : focus) {
+ String value = convertToString(item);
+ if (value != null)
+ result.add(new StringType(value).noExtensions());
+ }
+
+ if (result.size() > 1) {
+ throw makeException(exp, I18nConstants.FHIRPATH_NO_COLLECTION, "toString", result.size());
+ }
return result;
}
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 0bb10fd9d..e232cd803 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
@@ -1,340 +1,359 @@
-package org.hl7.fhir.r5.test;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.*;
-import java.util.stream.Stream;
-
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.commons.lang3.NotImplementedException;
-import org.fhir.ucum.UcumException;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.exceptions.PathEngineException;
-import org.hl7.fhir.r5.context.SimpleWorkerContext;
-import org.hl7.fhir.r5.elementmodel.Manager;
-import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
-import org.hl7.fhir.r5.fhirpath.ExpressionNode;
-import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
-import org.hl7.fhir.r5.fhirpath.TypeDetails;
-import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext;
-import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails;
-import org.hl7.fhir.r5.elementmodel.ValidatedFragment;
-import org.hl7.fhir.r5.formats.JsonParser;
-import org.hl7.fhir.r5.formats.XmlParser;
-import org.hl7.fhir.r5.model.*;
-import org.hl7.fhir.r5.test.utils.TestingUtilities;
-import org.hl7.fhir.utilities.Utilities;
-import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
-import org.hl7.fhir.utilities.npm.NpmPackage;
-import org.hl7.fhir.utilities.xml.XMLUtil;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.mock;
-
-public class FHIRPathTests {
-
- public enum TestResultType {OK, SYNTAX, SEMANTICS, EXECUTION}
-
- public class FHIRPathTestEvaluationServices implements IEvaluationContext {
-
- @Override
- public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstant), when item is element");
- }
-
- @Override
- public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstantType), when item is element");
- }
-
- @Override
- public boolean log(String argument, List focus) {
- return false;
- }
-
- @Override
- public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveFunction), when item is element (for " + functionName + ")");
- }
-
- @Override
- public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.checkFunction), when item is element");
- }
-
- @Override
- public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element");
- }
-
- @Override
- public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException {
- throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveReference), when item is element");
- }
-
- @Override
- public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException {
- if (url.equals("http://hl7.org/fhir/StructureDefinition/Patient"))
- return true;
- if (url.equals("http://hl7.org/fhir/StructureDefinition/Person"))
- return false;
- throw new FHIRException("unknown profile " + url);
-
- }
-
- @Override
- public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) {
- return context.fetchResource(ValueSet.class, url);
- }
-
- @Override
- public boolean paramIsType(String name, int index) {
- return false;
- }
- }
-
- private static FHIRPathEngine fp;
- private final Map resources = new HashMap();
- private static SimpleWorkerContext context;
-
- @BeforeAll
- public static void setUp() throws FileNotFoundException, FHIRException, IOException {
- context = new SimpleWorkerContext((SimpleWorkerContext) TestingUtilities.getSharedWorkerContext());
- if (!context.hasPackage("hl7.cda.us.ccda", null)) {
- FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build();
- NpmPackage npm = pcm.loadPackage("hl7.cda.uv.core", "2.0.0");
- context.loadFromPackage(npm, null);
- npm = pcm.loadPackage("hl7.cda.us.ccda", "current");
- context.loadFromPackage(npm, null);
- }
- if (fp == null) {
- fp = new FHIRPathEngine(context);
- }
- }
-
- public static Stream data() throws ParserConfigurationException, SAXException, IOException {
- Document dom = XMLUtil.parseToDom(TestingUtilities.loadTestResource("r5", "fhirpath", "tests-fhir-r5.xml"));
-
- List list = new ArrayList();
- List groups = new ArrayList();
- XMLUtil.getNamedChildren(dom.getDocumentElement(), "group", groups);
- for (Element g : groups) {
- XMLUtil.getNamedChildren(g, "test", list);
- XMLUtil.getNamedChildren(g, "modeTest", list);
- }
-
- List objects = new ArrayList<>();
- for (Element e : list) {
- objects.add(Arguments.of(getName(e), e));
- }
-
- return objects.stream();
- }
-
- private static Object getName(Element e) {
- String s = e.getAttribute("name");
- Element p = (Element) e.getParentNode();
- int ndx = 0;
- for (int i = 0; i < p.getChildNodes().getLength(); i++) {
- Node c = p.getChildNodes().item(i);
- if (c == e) {
- break;
- } else if (c instanceof Element) {
- ndx++;
- }
- }
- if (Utilities.noString(s)) {
- s = "?? - G " + p.getAttribute("name") + "[" + Integer.toString(ndx + 1) + "]";
- } else {
- s = s + " - G " + p.getAttribute("name") + "[" + Integer.toString(ndx + 1) + "]";
- }
- return s;
- }
-
- @SuppressWarnings("deprecation")
- @ParameterizedTest(name = "{index}: file {0}")
- @MethodSource("data")
- public void test(String name, Element test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException {
- // Setting timezone for this test. Grahame is in UTC+11, Travis is in GMT, and I'm here in Toronto, Canada with
- // all my time based tests failing locally...
- TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100"));
-
- fp.setHostServices(new FHIRPathTestEvaluationServices());
- String input = test.getAttribute("inputfile");
- String expression = XMLUtil.getNamedChild(test, "expression").getTextContent();
- TestResultType fail = TestResultType.OK;
- if ("syntax".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
- fail = TestResultType.SYNTAX;
- } else if ("semantic".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
- fail = TestResultType.SEMANTICS;
- } else if ("execution".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
- fail = TestResultType.EXECUTION;
- };
- fp.setAllowPolymorphicNames("lenient/polymorphics".equals(test.getAttribute("mode")));
- boolean skipStaticCheck = false;
- if ("true".equals(test.getAttribute("skipStaticCheck")))
- skipStaticCheck = true;
- Base res = null;
-
- List outcome = new ArrayList();
-
- System.out.println(name);
-
- ExpressionNode node = null;
- try {
- 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));
- }
-
- if (node != null) {
- if (!Utilities.noString(input)) {
- res = resources.get(input);
- if (res == null) {
- if ("cda".equals(test.getAttribute("mode"))) {
- res = Manager.makeParser(fp.getWorker(), FhirFormat.XML).parseSingle(TestingUtilities.loadTestResourceStream("r5", input), null);
- } else if (input.endsWith(".json")) {
- res = new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
- } else {
- res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
- }
- resources.put(input, res);
- }
- }
-
- if (!skipStaticCheck) {
- try {
- if (Utilities.noString(input)) {
- fp.check(null, null, node);
- } else {
- fp.check(res, res.fhirType(), res.fhirType(), node);
- }
- Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression));
- } catch (Exception e) {
- System.out.println("Checking Error: "+e.getMessage());
- Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression));
- node = null;
- }
- }
- }
-
- if (node != null) {
- try {
- if ("element".equals(test.getAttribute("mode"))) {
- List e = Manager.parse(fp.getWorker(), TestingUtilities.loadTestResourceStream("r5", input), input.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML);
- outcome = fp.evaluate(e.get(0).getElement(), node);
- } else {
- 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;
- }
- }
-
- if (fp.hasLog()) {
- System.out.println(name);
- System.out.println(fp.takeLog());
- }
-
- if (node != null) {
- if ("true".equals(test.getAttribute("predicate"))) {
- boolean ok = fp.convertToBoolean(outcome);
- outcome.clear();
- outcome.add(new BooleanType(ok));
- }
-
- List expected = new ArrayList();
- XMLUtil.getNamedChildren(test, "output", expected);
- assertEquals(outcome.size(), expected.size(), String.format("Expected %d objects but found %d for expression %s", expected.size(), outcome.size(), expression));
- if ("false".equals(test.getAttribute("ordered"))) {
- for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
- String tn = outcome.get(i).fhirType();
- String s;
- if (outcome.get(i) instanceof Quantity) {
- s = fp.convertToString(outcome.get(i));
- } else {
- s = ((PrimitiveType) outcome.get(i)).asStringValue();
- }
- boolean found = false;
- for (Element e : expected) {
- if ((Utilities.noString(e.getAttribute("type")) || e.getAttribute("type").equals(tn)) &&
- (Utilities.noString(e.getTextContent()) || e.getTextContent().equals(s))) {
- found = true;
- }
- }
- Assertions.assertTrue(found, String.format("Outcome %d: Value %s of type %s not expected for %s", i, s, tn, expression));
- }
- } else {
- for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
- String tn = expected.get(i).getAttribute("type");
- if (!Utilities.noString(tn)) {
- assertEquals(tn, outcome.get(i).fhirType(), String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType()));
- }
- String v = expected.get(i).getTextContent();
- if (!Utilities.noString(v)) {
- if (outcome.get(i) instanceof Quantity) {
- Quantity q = fp.parseQuantityString(v);
- Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()));
- } else {
- Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()));
- if (!(v.equals(((PrimitiveType) outcome.get(i)).fpValue()))) {
- System.out.println(name);
- System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression));
- }
- assertEquals(v, ((PrimitiveType) outcome.get(i)).fpValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression));
- }
- }
- }
- }
- }
- }
-
- @Test
- @DisplayName("resolveConstant returns a list of Base")
- public void resolveConstantReturnsList() {
- final String DUMMY_CONSTANT_1 = "dummyConstant1";
- final String DUMMY_CONSTANT_2 = "dummyConstant2";
- fp.setHostServices(new FHIRPathTestEvaluationServices() {
- @Override
- public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException {
-
- return Arrays.asList(
- new StringType(DUMMY_CONSTANT_1).noExtensions(),
- new StringType(DUMMY_CONSTANT_2).noExtensions());
- }
- });
-
- ExpressionNode expressionNode = fp.parse("%dummyConstant");
-
- List result = fp.evaluate(null, expressionNode);
- assertEquals(2, result.size());
- assertEquals(DUMMY_CONSTANT_1, result.get(0).primitiveValue());
- assertEquals(DUMMY_CONSTANT_2, result.get(1).primitiveValue());
- }
-
- @Test
- public void testEvaluate_Id() {
- Patient input = new Patient();
- input.setId(new IdType("http://base/Patient/123/_history/222"));
- List results = fp.evaluate(input, "Patient.id");
- assertEquals(1, results.size());
- assertEquals("123", results.get(0).toString());
- }
+package org.hl7.fhir.r5.test;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Stream;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.fhir.ucum.UcumException;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.exceptions.PathEngineException;
+import org.hl7.fhir.r5.context.SimpleWorkerContext;
+import org.hl7.fhir.r5.elementmodel.Manager;
+import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
+import org.hl7.fhir.r5.fhirpath.ExpressionNode;
+import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
+import org.hl7.fhir.r5.fhirpath.TypeDetails;
+import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext;
+import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails;
+import org.hl7.fhir.r5.elementmodel.ValidatedFragment;
+import org.hl7.fhir.r5.formats.JsonParser;
+import org.hl7.fhir.r5.formats.XmlParser;
+import org.hl7.fhir.r5.model.*;
+import org.hl7.fhir.r5.test.utils.TestingUtilities;
+import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
+import org.hl7.fhir.utilities.npm.NpmPackage;
+import org.hl7.fhir.utilities.xml.XMLUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class FHIRPathTests {
+
+ public enum TestResultType {OK, SYNTAX, SEMANTICS, EXECUTION}
+
+ public class FHIRPathTestEvaluationServices implements IEvaluationContext {
+
+ @Override
+ public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstant), when item is element");
+ }
+
+ @Override
+ public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) throws PathEngineException {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveConstantType), when item is element");
+ }
+
+ @Override
+ public boolean log(String argument, List focus) {
+ return false;
+ }
+
+ @Override
+ public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveFunction), when item is element (for " + functionName + ")");
+ }
+
+ @Override
+ public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, List parameters) throws PathEngineException {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.checkFunction), when item is element");
+ }
+
+ @Override
+ public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, List> parameters) {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element");
+ }
+
+ @Override
+ public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) throws FHIRException {
+ throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.resolveReference), when item is element");
+ }
+
+ @Override
+ public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) throws FHIRException {
+ if (url.equals("http://hl7.org/fhir/StructureDefinition/Patient"))
+ return true;
+ if (url.equals("http://hl7.org/fhir/StructureDefinition/Person"))
+ return false;
+ throw new FHIRException("unknown profile " + url);
+
+ }
+
+ @Override
+ public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) {
+ return context.fetchResource(ValueSet.class, url);
+ }
+
+ @Override
+ public boolean paramIsType(String name, int index) {
+ return false;
+ }
+ }
+
+ private static FHIRPathEngine fp;
+ private final Map resources = new HashMap();
+ private static SimpleWorkerContext context;
+
+ @BeforeAll
+ public static void setUp() throws FileNotFoundException, FHIRException, IOException {
+ context = new SimpleWorkerContext((SimpleWorkerContext) TestingUtilities.getSharedWorkerContext());
+ if (!context.hasPackage("hl7.cda.us.ccda", null)) {
+ FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build();
+ NpmPackage npm = pcm.loadPackage("hl7.cda.uv.core", "2.0.0");
+ context.loadFromPackage(npm, null);
+ npm = pcm.loadPackage("hl7.cda.us.ccda", "current");
+ context.loadFromPackage(npm, null);
+ }
+ if (fp == null) {
+ fp = new FHIRPathEngine(context);
+ }
+ }
+
+ public static Stream data() throws ParserConfigurationException, SAXException, IOException {
+ Document dom = XMLUtil.parseToDom(TestingUtilities.loadTestResource("r5", "fhirpath", "tests-fhir-r5.xml"));
+
+ List list = new ArrayList();
+ List groups = new ArrayList();
+ XMLUtil.getNamedChildren(dom.getDocumentElement(), "group", groups);
+ for (Element g : groups) {
+ XMLUtil.getNamedChildren(g, "test", list);
+ XMLUtil.getNamedChildren(g, "modeTest", list);
+ }
+
+ List objects = new ArrayList<>();
+ for (Element e : list) {
+ objects.add(Arguments.of(getName(e), e));
+ }
+
+ return objects.stream();
+ }
+
+ private static Object getName(Element e) {
+ String s = e.getAttribute("name");
+ Element p = (Element) e.getParentNode();
+ int ndx = 0;
+ for (int i = 0; i < p.getChildNodes().getLength(); i++) {
+ Node c = p.getChildNodes().item(i);
+ if (c == e) {
+ break;
+ } else if (c instanceof Element) {
+ ndx++;
+ }
+ }
+ if (Utilities.noString(s)) {
+ s = "?? - G " + p.getAttribute("name") + "[" + Integer.toString(ndx + 1) + "]";
+ } else {
+ s = s + " - G " + p.getAttribute("name") + "[" + Integer.toString(ndx + 1) + "]";
+ }
+ return s;
+ }
+
+ @SuppressWarnings("deprecation")
+ @ParameterizedTest(name = "{index}: file {0}")
+ @MethodSource("data")
+ public void test(String name, Element test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException {
+ // Setting timezone for this test. Grahame is in UTC+11, Travis is in GMT, and I'm here in Toronto, Canada with
+ // all my time based tests failing locally...
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100"));
+
+ fp.setHostServices(new FHIRPathTestEvaluationServices());
+ String input = test.getAttribute("inputfile");
+ String expression = XMLUtil.getNamedChild(test, "expression").getTextContent();
+ TestResultType fail = TestResultType.OK;
+ if ("syntax".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
+ fail = TestResultType.SYNTAX;
+ } else if ("semantic".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
+ fail = TestResultType.SEMANTICS;
+ } else if ("execution".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) {
+ fail = TestResultType.EXECUTION;
+ };
+ fp.setAllowPolymorphicNames("lenient/polymorphics".equals(test.getAttribute("mode")));
+ boolean skipStaticCheck = false;
+ if ("true".equals(test.getAttribute("skipStaticCheck")))
+ skipStaticCheck = true;
+ Base res = null;
+
+ List outcome = new ArrayList();
+
+ System.out.println(name);
+
+ ExpressionNode node = null;
+ try {
+ 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));
+ }
+
+ if (node != null) {
+ if (!Utilities.noString(input)) {
+ res = resources.get(input);
+ if (res == null) {
+ if ("cda".equals(test.getAttribute("mode"))) {
+ res = Manager.makeParser(fp.getWorker(), FhirFormat.XML).parseSingle(TestingUtilities.loadTestResourceStream("r5", input), null);
+ } else if (input.endsWith(".json")) {
+ res = new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
+ } else {
+ res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
+ }
+ resources.put(input, res);
+ }
+ }
+
+ if (!skipStaticCheck) {
+ try {
+ if (Utilities.noString(input)) {
+ fp.check(null, null, node);
+ } else {
+ fp.check(res, res.fhirType(), res.fhirType(), node);
+ }
+ Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression));
+ } catch (Exception e) {
+ System.out.println("Checking Error: "+e.getMessage());
+ Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression));
+ node = null;
+ }
+ }
+ }
+
+ if (node != null) {
+ try {
+ if ("element".equals(test.getAttribute("mode"))) {
+ List e = Manager.parse(fp.getWorker(), TestingUtilities.loadTestResourceStream("r5", input), input.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML);
+ outcome = fp.evaluate(e.get(0).getElement(), node);
+ } else {
+ 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;
+ }
+ }
+
+ if (fp.hasLog()) {
+ System.out.println(name);
+ System.out.println(fp.takeLog());
+ }
+
+ if (node != null) {
+ if ("true".equals(test.getAttribute("predicate"))) {
+ boolean ok = fp.convertToBoolean(outcome);
+ outcome.clear();
+ outcome.add(new BooleanType(ok));
+ }
+
+ List expected = new ArrayList();
+ XMLUtil.getNamedChildren(test, "output", expected);
+ assertEquals(outcome.size(), expected.size(), String.format("Expected %d objects but found %d for expression %s", expected.size(), outcome.size(), expression));
+ if ("false".equals(test.getAttribute("ordered"))) {
+ for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
+ String tn = outcome.get(i).fhirType();
+ String s;
+ if (outcome.get(i) instanceof Quantity) {
+ s = fp.convertToString(outcome.get(i));
+ } else {
+ s = ((PrimitiveType) outcome.get(i)).asStringValue();
+ }
+ boolean found = false;
+ for (Element e : expected) {
+ if ((Utilities.noString(e.getAttribute("type")) || e.getAttribute("type").equals(tn)) &&
+ (Utilities.noString(e.getTextContent()) || e.getTextContent().equals(s))) {
+ found = true;
+ }
+ }
+ Assertions.assertTrue(found, String.format("Outcome %d: Value %s of type %s not expected for %s", i, s, tn, expression));
+ }
+ } else {
+ for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
+ String tn = expected.get(i).getAttribute("type");
+ if (!Utilities.noString(tn)) {
+ assertEquals(tn, outcome.get(i).fhirType(), String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType()));
+ }
+ String v = expected.get(i).getTextContent();
+ if (!Utilities.noString(v)) {
+ if (outcome.get(i) instanceof Quantity) {
+ Quantity q = fp.parseQuantityString(v);
+ Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()));
+ } else {
+ Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()));
+ if (!(v.equals(((PrimitiveType) outcome.get(i)).fpValue()))) {
+ System.out.println(name);
+ System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression));
+ }
+ assertEquals(v, ((PrimitiveType) outcome.get(i)).fpValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ @DisplayName("resolveConstant returns a list of Base")
+ public void resolveConstantReturnsList() {
+ final String DUMMY_CONSTANT_1 = "dummyConstant1";
+ final String DUMMY_CONSTANT_2 = "dummyConstant2";
+ fp.setHostServices(new FHIRPathTestEvaluationServices() {
+ @Override
+ public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException {
+
+ return Arrays.asList(
+ new StringType(DUMMY_CONSTANT_1).noExtensions(),
+ new StringType(DUMMY_CONSTANT_2).noExtensions());
+ }
+ });
+
+ ExpressionNode expressionNode = fp.parse("%dummyConstant");
+
+ List result = fp.evaluate(null, expressionNode);
+ assertEquals(2, result.size());
+ assertEquals(DUMMY_CONSTANT_1, result.get(0).primitiveValue());
+ assertEquals(DUMMY_CONSTANT_2, result.get(1).primitiveValue());
+ }
+
+ @Test
+ public void testEvaluate_Id() {
+ Patient input = new Patient();
+ input.setId(new IdType("http://base/Patient/123/_history/222"));
+ List results = fp.evaluate(input, "Patient.id");
+ assertEquals(1, results.size());
+ assertEquals("123", results.get(0).toString());
+ }
+
+ @Test
+ public void testEvaluate_ToStringOnDateValue() {
+ Patient input = new Patient();
+ var dtv = new DateType("2024");
+ input.setBirthDateElement(dtv);
+ List results = fp.evaluate(input, "Patient.birthDate.toString()");
+ assertEquals(1, results.size());
+ assertEquals("2024", results.get(0).toString());
+ }
+
+ @Test
+ public void testEvaluate_ToStringOnExtensionOnlyValue() {
+ Patient input = new Patient();
+ var dtv = new DateType();
+ input.setBirthDateElement(dtv);
+ List results = fp.evaluate(input, "Patient.birthDate.toString()");
+ assertEquals(0, results.size());
+ }
}
\ No newline at end of file