mirror of
https://github.com/hapifhir/org.hl7.fhir.core.git
synced 2025-03-01 17:09:08 +00:00
FHIRPath - support lenient mode on polymorphics
This commit is contained in:
parent
8a11bd2e9b
commit
66f0b35a38
@ -0,0 +1,5 @@
|
||||
Validator:
|
||||
* No changes
|
||||
|
||||
Other code changes:
|
||||
* Support lenient mode on FIHRPath when referring to polymorphics
|
@ -105,6 +105,7 @@ import ca.uhn.fhir.util.ElementUtil;
|
||||
*
|
||||
*/
|
||||
public class FHIRPathEngine {
|
||||
|
||||
private enum Equality { Null, True, False }
|
||||
|
||||
private class FHIRConstant extends Base {
|
||||
@ -211,6 +212,7 @@ public class FHIRPathEngine {
|
||||
private ValidationOptions terminologyServiceOptions = new ValidationOptions();
|
||||
private ProfileUtilities profileUtilities;
|
||||
private String location; // for error messages
|
||||
private boolean allowPolymorphicNames;
|
||||
|
||||
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
|
||||
// the application can implement them by providing a constant resolver
|
||||
@ -372,10 +374,24 @@ public class FHIRPathEngine {
|
||||
* @throws FHIRException
|
||||
*/
|
||||
protected void getChildrenByName(Base item, String name, List<Base> result) throws FHIRException {
|
||||
String tn = null;
|
||||
if (isAllowPolymorphicNames()) {
|
||||
// we'll look to see whether we hav a polymorphic name
|
||||
for (Property p : item.children()) {
|
||||
if (p.getName().endsWith("[x]")) {
|
||||
String n = p.getName().substring(0, p.getName().length()-3);
|
||||
if (name.startsWith(n)) {
|
||||
tn = name.substring(n.length());
|
||||
name = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Base[] list = item.listChildrenByName(name, false);
|
||||
if (list != null) {
|
||||
for (Base v : list) {
|
||||
if (v != null) {
|
||||
if (v != null && (tn == null || v.fhirType().equalsIgnoreCase(tn))) {
|
||||
result.add(v);
|
||||
}
|
||||
}
|
||||
@ -5166,7 +5182,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, isAllowPolymorphicNames());
|
||||
if (ed != null) {
|
||||
if (!Utilities.noString(ed.getFixedType()))
|
||||
result.addType(ed.getFixedType());
|
||||
@ -5556,5 +5572,14 @@ public class FHIRPathEngine {
|
||||
public IWorkerContext getWorker() {
|
||||
return worker;
|
||||
}
|
||||
|
||||
public boolean isAllowPolymorphicNames() {
|
||||
return allowPolymorphicNames;
|
||||
}
|
||||
|
||||
public void setAllowPolymorphicNames(boolean allowPolymorphicNames) {
|
||||
this.allowPolymorphicNames = allowPolymorphicNames;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.Quantity;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.TypeDetails;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.test.FHIRPathTests.TestResultType;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
||||
@ -41,6 +42,8 @@ import org.xml.sax.SAXException;
|
||||
|
||||
public class FHIRPathTests {
|
||||
|
||||
public enum TestResultType {OK, SYNTAX, SEMANTICS, EXECUTION}
|
||||
|
||||
public class FHIRPathTestEvaluationServices implements IEvaluationContext {
|
||||
|
||||
@Override
|
||||
@ -111,6 +114,7 @@ public class FHIRPathTests {
|
||||
XMLUtil.getNamedChildren(dom.getDocumentElement(), "group", groups);
|
||||
for (Element g : groups) {
|
||||
XMLUtil.getNamedChildren(g, "test", list);
|
||||
XMLUtil.getNamedChildren(g, "modeTest", list);
|
||||
}
|
||||
|
||||
List<Arguments> objects = new ArrayList<>();
|
||||
@ -147,84 +151,115 @@ public class FHIRPathTests {
|
||||
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"));
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100"));
|
||||
|
||||
fp.setHostServices(new FHIRPathTestEvaluationServices());
|
||||
String input = test.getAttribute("inputfile");
|
||||
String expression = XMLUtil.getNamedChild(test, "expression").getTextContent();
|
||||
boolean fail = Utilities.existsInList(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"), "true", "semantic");
|
||||
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")));
|
||||
Resource res = null;
|
||||
|
||||
List<Base> outcome = new ArrayList<Base>();
|
||||
|
||||
ExpressionNode node = fp.parse(expression);
|
||||
System.out.println(name);
|
||||
|
||||
ExpressionNode node = null;
|
||||
try {
|
||||
if (Utilities.noString(input)) {
|
||||
fp.check(null, null, node);
|
||||
} else {
|
||||
res = resources.get(input);
|
||||
if (res == null) {
|
||||
res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
|
||||
resources.put(input, res);
|
||||
}
|
||||
fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node);
|
||||
}
|
||||
outcome = fp.evaluate(res, node);
|
||||
Assertions.assertFalse(fail, String.format("Expected exception parsing %s", expression));
|
||||
node = fp.parse(expression);
|
||||
Assertions.assertTrue(fail != TestResultType.SYNTAX, String.format("Expected exception didn't occur parsing %s", expression));
|
||||
} catch (Exception e) {
|
||||
Assertions.assertTrue(fail, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression));
|
||||
Assertions.assertTrue(fail == TestResultType.SYNTAX, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression));
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
try {
|
||||
if (Utilities.noString(input)) {
|
||||
fp.check(null, null, node);
|
||||
} else {
|
||||
res = resources.get(input);
|
||||
if (res == null) {
|
||||
res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input));
|
||||
resources.put(input, res);
|
||||
}
|
||||
fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node);
|
||||
}
|
||||
Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression));
|
||||
} catch (Exception e) {
|
||||
Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression));
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
try {
|
||||
outcome = fp.evaluate(res, node);
|
||||
Assertions.assertTrue(fail == TestResultType.OK, String.format("Expected exception didn't occur executing %s", expression));
|
||||
} catch (Exception e) {
|
||||
Assertions.assertTrue(fail == TestResultType.EXECUTION, String.format("Unexpected exception executing %s: " + e.getMessage(), expression));
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ("true".equals(test.getAttribute("predicate"))) {
|
||||
boolean ok = fp.convertToBoolean(outcome);
|
||||
outcome.clear();
|
||||
outcome.add(new BooleanType(ok));
|
||||
}
|
||||
System.out.println(name);
|
||||
if (fp.hasLog()) {
|
||||
System.out.println(name);
|
||||
System.out.println(fp.takeLog());
|
||||
}
|
||||
|
||||
List<Element> expected = new ArrayList<Element>();
|
||||
XMLUtil.getNamedChildren(test, "output", expected);
|
||||
Assertions.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));
|
||||
if (node != null) {
|
||||
if ("true".equals(test.getAttribute("predicate"))) {
|
||||
boolean ok = fp.convertToBoolean(outcome);
|
||||
outcome.clear();
|
||||
outcome.add(new BooleanType(ok));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
|
||||
String tn = expected.get(i).getAttribute("type");
|
||||
if (!Utilities.noString(tn)) {
|
||||
Assertions.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)) {
|
||||
|
||||
List<Element> expected = new ArrayList<Element>();
|
||||
XMLUtil.getNamedChildren(test, "output", expected);
|
||||
Assertions.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) {
|
||||
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()));
|
||||
s = fp.convertToString(outcome.get(i));
|
||||
} 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)).asStringValue()))) {
|
||||
System.out.println(name);
|
||||
System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
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)) {
|
||||
Assertions.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)).asStringValue()))) {
|
||||
System.out.println(name);
|
||||
System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
}
|
||||
Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
}
|
||||
Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
pom.xml
2
pom.xml
@ -17,7 +17,7 @@
|
||||
|
||||
<properties>
|
||||
<hapi_fhir_version>5.1.0</hapi_fhir_version>
|
||||
<validator_test_case_version>1.1.40</validator_test_case_version>
|
||||
<validator_test_case_version>1.1.41</validator_test_case_version>
|
||||
<junit_jupiter_version>5.6.2</junit_jupiter_version>
|
||||
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
|
||||
<jacoco_version>0.8.5</jacoco_version>
|
||||
|
Loading…
x
Reference in New Issue
Block a user