diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 1447b7469..c218f4c6b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -1098,6 +1098,10 @@ public abstract class BaseWorkerContext implements IWorkerContext { @Override public String oid2Uri(String oid) { synchronized (lock) { + if (oid != null && oid.startsWith("urn:oid:")) { + oid = oid.substring(8); + } + String uri = OIDUtils.getUriForOid(oid); if (uri != null) return uri; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index dc1c242b8..e7e03c860 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -248,6 +248,7 @@ public interface IWorkerContext { * @throws FHIRException */ public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException; + public void generateSnapshot(StructureDefinition mr, boolean ifLogical); // -- Terminology services ------------------------------------------------------ @@ -543,4 +544,5 @@ public interface IWorkerContext { public String getLinkForUrl(String corePath, String s); + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 725a08b74..7f0657ee1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -593,10 +593,12 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon return r; } + @Override public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException { generateSnapshot(p, false); } + @Override public void generateSnapshot(StructureDefinition p, boolean logical) throws DefinitionException, FHIRException { if (!p.hasSnapshot() && (logical || p.getKind() != StructureDefinitionKind.LOGICAL)) { if (!p.hasBaseDefinition()) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java index 02d9fee88..b1734497e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java @@ -176,7 +176,7 @@ public class Property { if (definition.getType().size() > 0) return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode())); else - return !definition.getPath().contains(".") && structure.getKind() == StructureDefinitionKind.RESOURCE; + return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE || structure.getKind() == StructureDefinitionKind.LOGICAL); } public boolean isList() { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java index c7e1bdb26..f0dd0996d 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java @@ -17,6 +17,8 @@ import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.formats.IParser.OutputStyle; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionDifferentialComponent; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.utilities.cache.PackageCacheManager; @@ -25,158 +27,184 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import junit.framework.Assert; + public class CDARoundTripTests { - private SimpleWorkerContext context; - private FHIRPathEngine fp; +// private SimpleWorkerContext context; +// old-test private FHIRPathEngine fp; @Before public void setUp() throws Exception { - context = new SimpleWorkerContext(); - PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); - context.loadFromPackage(pcm.loadPackage("hl7.fhir.core", "dev"), null, "StructureDefinition"); - context.loadFromPackage(pcm.loadPackage("hl7.fhir.cda", "dev"), null, "StructureDefinition"); - fp = new FHIRPathEngine(context); +// old-test context = new SimpleWorkerContext(); +// old-test PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); +// old-test context.loadFromPackage(pcm.loadPackage("hl7.fhir.core", "dev"), null, "StructureDefinition"); +// old-test context.loadFromPackage(pcm.loadPackage("hl7.fhir.cda", "dev"), null, "StructureDefinition"); +// old-test fp = new FHIRPathEngine(context); } +// old-test +// @Test +// public void testCDA() throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { +// try { +// +// InputStream fileSource = TestingUtilities.loadTestResourceStream("cda", "cda-original.xml"); +// String roundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.xml"); +// String jsonRoundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.json"); +// +// Element e = Manager.parse(context, fileSource, FhirFormat.XML); +// +// Manager.compose(context, e, new FileOutputStream(roundTrip), FhirFormat.XML, OutputStyle.PRETTY, null); +// Manager.compose(context, e, new FileOutputStream(jsonRoundTrip), FhirFormat.JSON, OutputStyle.PRETTY, null); +// +//// +//// assertEquals("POCD_HD000040", fp.evaluateToString(e, "typeId.extension")); +//// assertEquals("2.16.840.1.113883.1.3", fp.evaluateToString(e, "typeId.root")); +//// +//// assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(e, "templateId.root")); +//// +// assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(e, "id.root")); +// assertEquals("c266", fp.evaluateToString(e, "id.extension")); +// +//// Good Health Clinic Consultation Note +// assertEquals("Good Health Clinic Consultation Note", fp.evaluateToString(e, "title.dataString")); +//// +// assertEquals("2000-04-07", fp.evaluateToString(e, "effectiveTime.value")); +//// +// assertEquals("N", fp.evaluateToString(e, "confidentialityCode.code")); +// assertEquals("2.16.840.1.113883.5.25", fp.evaluateToString(e, "confidentialityCode.codeSystem")); +//// +// assertEquals("en-US", fp.evaluateToString(e, "languageCode.code")); +//// +// assertEquals("BB35", fp.evaluateToString(e, "setId.extension")); +// assertEquals("2.16.840.1.113883.19.7", fp.evaluateToString(e, "setId.root")); +//// +// assertEquals("2", fp.evaluateToString(e, "versionNumber.value")); +//// +//// +//// +// assertEquals("12345", fp.evaluateToString(e, "recordTarget.patientRole.id.extension")); +// assertEquals("2.16.840.1.113883.19.5", fp.evaluateToString(e, "recordTarget.patientRole.id.root")); +//// +//// +//// Levin +// assertEquals("Levin", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.family.dataString")); +//// Henry +// assertEquals("Henry", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.given.dataString")); +//// the 7th +//// +//// +//// +//// +//// +//// +//// +//// +//// +// +//// +//// +//// +////
+// +//// +////
+//// +//// Skin Exam +//// Erythematous rash, palmar surface, left index finger. +//// +//// +// +// assertEquals("Skin Exam", fp.evaluateToString(e, "component.structuredBody.component.section.component.section.where(code.code='8709-8' and code.codeSystem='2.16.840.1.113883.6.1').title.dataString")); +// //
Erythematous rash, palmar surface, left index finger. +// //
+// String text = fp.evaluateToString(e, "component.structuredBody.component.section.component.section.where(code.code='8709-8' and code.codeSystem='2.16.840.1.113883.6.1').text"); +// assertTrue(text.contains("")); +// } catch (Exception e) { +// System.out.println(e.getMessage()); +// e.printStackTrace(); +// throw e; +// } +// } +// +// @Ignore +// public void testDCI() throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { +// try { +// Element e = Manager.parse(context, +// new FileInputStream("C:\\work\\org.hl7.fhir.us\\ccda-to-fhir-maps\\cda\\IAT2-Discharge_Summary-DCI.xml"), +// FhirFormat.XML); +// +// Manager.compose(context, e, new FileOutputStream("C:\\temp\\ccda.xml"), FhirFormat.XML, OutputStyle.PRETTY, null); +//// Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge_Summary-DCI.out.json"), FhirFormat.JSON, OutputStyle.PRETTY, null); +//// Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge_Summary-DCI.out.ttl"), FhirFormat.TURTLE, OutputStyle.PRETTY, null); +// } catch (Exception e) { +// System.out.println(e.getMessage()); +// e.printStackTrace(); +// throw e; +// } +// } +// +// @Ignore +// public void testEpic() +// throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { +// Element e = Manager.parse(context, +// new FileInputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.xml"), +// FhirFormat.XML); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.xml"), +// FhirFormat.XML, OutputStyle.PRETTY, null); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.json"), +// FhirFormat.JSON, OutputStyle.PRETTY, null); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.ttl"), +// FhirFormat.TURTLE, OutputStyle.PRETTY, null); +// } +// +// @Ignore +// public void testDHIT() +// throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { +// Element e = Manager.parse(context, +// new FileInputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.xml"), +// FhirFormat.XML); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.xml"), +// FhirFormat.XML, OutputStyle.PRETTY, null); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.json"), +// FhirFormat.JSON, OutputStyle.PRETTY, null); +// Manager.compose(context, e, +// new FileOutputStream( +// "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.ttl"), +// FhirFormat.TURTLE, OutputStyle.PRETTY, null); +// } + + @Test - public void testCDA() throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { - try { - - InputStream fileSource = TestingUtilities.loadTestResourceStream("cda", "cda-original.xml"); - String roundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.xml"); - String jsonRoundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.json"); - - Element e = Manager.parse(context, fileSource, FhirFormat.XML); - - Manager.compose(context, e, new FileOutputStream(roundTrip), FhirFormat.XML, OutputStyle.PRETTY, null); - Manager.compose(context, e, new FileOutputStream(jsonRoundTrip), FhirFormat.JSON, OutputStyle.PRETTY, null); - -// -// assertEquals("POCD_HD000040", fp.evaluateToString(e, "typeId.extension")); -// assertEquals("2.16.840.1.113883.1.3", fp.evaluateToString(e, "typeId.root")); -// -// assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(e, "templateId.root")); -// - assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(e, "id.root")); - assertEquals("c266", fp.evaluateToString(e, "id.extension")); - -// Good Health Clinic Consultation Note - assertEquals("Good Health Clinic Consultation Note", fp.evaluateToString(e, "title.dataString")); -// - assertEquals("2000-04-07", fp.evaluateToString(e, "effectiveTime.value")); -// - assertEquals("N", fp.evaluateToString(e, "confidentialityCode.code")); - assertEquals("2.16.840.1.113883.5.25", fp.evaluateToString(e, "confidentialityCode.codeSystem")); -// - assertEquals("en-US", fp.evaluateToString(e, "languageCode.code")); -// - assertEquals("BB35", fp.evaluateToString(e, "setId.extension")); - assertEquals("2.16.840.1.113883.19.7", fp.evaluateToString(e, "setId.root")); -// - assertEquals("2", fp.evaluateToString(e, "versionNumber.value")); -// -// -// - assertEquals("12345", fp.evaluateToString(e, "recordTarget.patientRole.id.extension")); - assertEquals("2.16.840.1.113883.19.5", fp.evaluateToString(e, "recordTarget.patientRole.id.root")); -// -// -// Levin - assertEquals("Levin", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.family.dataString")); -// Henry - assertEquals("Henry", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.given.dataString")); -// the 7th -// -// -// -// -// -// -// -// -// - -// -// -// -//
- -// -//
-// -// Skin Exam -// Erythematous rash, palmar surface, left index finger. -// -// - - assertEquals("Skin Exam", fp.evaluateToString(e, "component.structuredBody.component.section.component.section.where(code.code='8709-8' and code.codeSystem='2.16.840.1.113883.6.1').title.dataString")); - //
Erythematous rash, palmar surface, left index finger. - //
- String text = fp.evaluateToString(e, "component.structuredBody.component.section.component.section.where(code.code='8709-8' and code.codeSystem='2.16.840.1.113883.6.1').text"); - assertTrue(text.contains("")); - } catch (Exception e) { - System.out.println(e.getMessage()); - e.printStackTrace(); - throw e; + public void testSimple() throws IOException { + PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); + SimpleWorkerContext context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1")); + context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "any.xml"), "any.xml", null); + context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ii.xml"), "ii.xml", null); + context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cd.xml"), "cd.xml", null); + context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ce.xml"), "ce.xml", null); + context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cda.xml"), "cda.xml", null); + for (StructureDefinition sd : context.getStructures()) { + if (!sd.hasSnapshot()) { + System.out.println("generate snapshot for "+sd.getUrl()); + context.generateSnapshot(sd, true); + } } + Element cda = Manager.parse(context, TestingUtilities.loadTestResourceStream("r5", "cda", "example.xml"), FhirFormat.XML); + FHIRPathEngine fp = new FHIRPathEngine(context); + Assert.assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(null, cda, cda, cda, fp.parse("ClinicalDocument.templateId.root"))); + } - - @Ignore - public void testDCI() throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { - try { - Element e = Manager.parse(context, - new FileInputStream("C:\\work\\org.hl7.fhir.us\\ccda-to-fhir-maps\\cda\\IAT2-Discharge_Summary-DCI.xml"), - FhirFormat.XML); - - Manager.compose(context, e, new FileOutputStream("C:\\temp\\ccda.xml"), FhirFormat.XML, OutputStyle.PRETTY, null); -// Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge_Summary-DCI.out.json"), FhirFormat.JSON, OutputStyle.PRETTY, null); -// Manager.compose(context, e, new FileOutputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge_Summary-DCI.out.ttl"), FhirFormat.TURTLE, OutputStyle.PRETTY, null); - } catch (Exception e) { - System.out.println(e.getMessage()); - e.printStackTrace(); - throw e; - } - } - - @Ignore - public void testEpic() - throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { - Element e = Manager.parse(context, - new FileInputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.xml"), - FhirFormat.XML); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.xml"), - FhirFormat.XML, OutputStyle.PRETTY, null); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.json"), - FhirFormat.JSON, OutputStyle.PRETTY, null); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-Discharge-Homework-Epic.out.ttl"), - FhirFormat.TURTLE, OutputStyle.PRETTY, null); - } - - @Ignore - public void testDHIT() - throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { - Element e = Manager.parse(context, - new FileInputStream("C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.xml"), - FhirFormat.XML); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.xml"), - FhirFormat.XML, OutputStyle.PRETTY, null); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.json"), - FhirFormat.JSON, OutputStyle.PRETTY, null); - Manager.compose(context, e, - new FileOutputStream( - "C:\\work\\org.hl7.fhir.test\\ccda-to-fhir-maps\\testdocuments\\IAT2-DS-Homework-DHIT.out.ttl"), - FhirFormat.TURTLE, OutputStyle.PRETTY, null); - } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java index 0ac3d15c0..347208053 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java @@ -31,6 +31,9 @@ public class OIDUtils { */ public static String getUriForOid(String r) { + if (r == null) { + return null; + } if (r.equals("2.16.840.1.113883.6.96")) return "http://snomed.info/sct"; if (r.equals("2.16.840.1.113883.6.1")) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index 1f9c48293..d7d14d4e6 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -135,7 +135,10 @@ public class VersionUtilities { if (version == null) return null; - if (Utilities.charCount(version, '.') == 2) { + if (Utilities.charCount(version, '.') == 1) { + String[] p = version.split("\\."); + return p[0]+"."+p[1]; + } else if (Utilities.charCount(version, '.') == 2) { String[] p = version.split("\\."); return p[0]+"."+p[1]; } else { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java index 0fde15287..af0282ede 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java @@ -31,7 +31,7 @@ public class ValidationOptions { public boolean isGuessSystem() { return guessSystem; } - + private ValidationOptions copy() { ValidationOptions n = new ValidationOptions(language); n.useServer = useServer; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java index 6779ea132..48d16bca1 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java @@ -84,6 +84,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.ConstraintSeverity; import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; @@ -112,6 +113,7 @@ import org.hl7.fhir.r5.model.SampledData; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComponent; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.TimeType; @@ -166,6 +168,7 @@ import ca.uhn.fhir.util.ObjectUtil; public class InstanceValidator extends BaseValidator implements IResourceValidator { + public class ValidatorHostContext { private Object appContext; private Element container; // bundle, or parameters @@ -1135,6 +1138,245 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return res; } + private boolean checkTerminologyCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { + boolean res = true; + if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing (cc)")) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + try { + CodeableConcept cc = convertToCodeableConcept(element, logical); + if (!cc.hasCoding()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl()+")"); + else + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+")"); + } + } else { + long t = System.nanoTime(); + + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = "+vr.getErrorClass().toString()+")"); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = "+vr.getErrorClass().toString()+")"); + } else if (binding.getStrength() == BindingStrength.PREFERRED) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = "+vr.getErrorClass().toString()+")"); + } else { + if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required) (codes = "+ccSummary(cc)+")"); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); + if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = "+ccSummary(cc)+")"); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = "+ccSummary(cc)+")"); + } + } + } else if (vr.getMessage()!=null) { + res = false; + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + res = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + String nextCode = nextCoding.getCode(); + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); + if (!vr.isOk()) { + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); + } + } + } + } + txTime = txTime + (System.nanoTime() - t); + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error "+e.getMessage()+" validating CodeableConcept"); + } + // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. + if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { + checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + } else if (!noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + } + } + } + return res; + } + + private void checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { + Coding c = convertToCoding(element, logical); + String code = c.getCode(); + String system = c.getSystem(); + String display = c.getDisplay(); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); + + if (system != null && code != null && !noTerminologyChecks) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")"); + try { + if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) + if (theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + try { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required)"); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"); + } else if (binding.getStrength() == BindingStrength.PREFERRED) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set)"); + } else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack); + else + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); + } else if (binding.getStrength() == BindingStrength.PREFERRED) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : "")); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error "+e.getMessage()+" validating Coding"); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + } + } + } + } catch (Exception e) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error "+e.getMessage()+" validating Coding: " + e.toString()); + } + } + } + + private CodeableConcept convertToCodeableConcept(Element element, StructureDefinition logical) { + CodeableConcept res = new CodeableConcept(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.getCodingFirstRep().setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.getCodingFirstRep().setSystem(url); + } else { + res.getCodingFirstRep().setSystem("urn:oid:"+oid); + } + } else if ("Coding.version".equals(m)) { + res.getCodingFirstRep().setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.getCodingFirstRep().setDisplay(list.get(0).primitiveValue()); + } else if ("CodeableConcept.text".equals(m)) { + res.setText(list.get(0).primitiveValue()); + } else if ("CodeableConcept.coding".equals(m)) { + StructureDefinition c = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode()); + for (Element e : list) { + res.addCoding(convertToCoding(e, c)); + } + } + } + } + } + } + return res; + } + + private Coding convertToCoding(Element element, StructureDefinition logical) { + Coding res = new Coding(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.setSystem(url); + } else { + res.setSystem("urn:oid:"+oid); + } + } else if ("Coding.version".equals(m)) { + res.setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.setDisplay(list.get(0).primitiveValue()); + } + } + } + } + } + return res; + } + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); @@ -4194,7 +4436,18 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L } else if (type.equals("Resource")) { validateContains(hostContext, errors, ei.path, ei.definition, definition, resource, ei.element, localStack, idStatusForEntry(element, ei)); // if // (str.matches(".*([.,/])work\\1$")) - } + } else if (Utilities.isAbsoluteUrl(type)) { + StructureDefinition defn = context.fetchTypeDefinition(type); + if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) { + List txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep()); + if (txtype.contains("CodeableConcept")) { + checkTerminologyCodeableConcept(errors, ei.path, ei.element, profile, ei.definition, stack, defn); + thisIsCodeableConcept = true; + } else if (txtype.contains("Coding")) { + checkTerminologyCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn); + } + } + } } else { if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.name)) validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, false, true); @@ -4283,6 +4536,44 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L } } + private boolean hasMapping(String url, StructureDefinition defn, ElementDefinition elem) { + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + return true; + } + } + + } + return false; + } + + private List getMapping(String url, StructureDefinition defn, ElementDefinition elem) { + List res = new ArrayList<>(); + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + res.add(m.getMap()); + } + } + } + return res; + } + public void checkMustSupport(StructureDefinition profile, ElementInfo ei) { String usesMustSupport = profile.getUserString("usesMustSupport"); if (usesMustSupport == null) { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 5f6c29155..0f2f1d2be 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -23,11 +23,14 @@ 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.r5.utils.FHIRPathEngine.IEvaluationContext; +import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy; import org.hl7.fhir.r5.validation.InstanceValidator; import org.hl7.fhir.r5.validation.ValidationEngine; +import org.hl7.fhir.utilities.VersionUtilities; +import org.hl7.fhir.utilities.json.JSONUtil; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.validation.tests.utilities.TestUtilities; @@ -72,7 +75,6 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour private String name; private JsonObject content; - private static String veVersion; public ValidationTestSuite(String name, JsonObject content) { this.name = name; @@ -81,7 +83,8 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour private static final String DEF_TX = "http://tx.fhir.org"; // private static final String DEF_TX = "http://local.fhir.org:960"; - private static ValidationEngine ve; + private static Map ve = new HashMap<>(); + private static ValidationEngine vCurr; @SuppressWarnings("deprecation") @Test @@ -92,27 +95,28 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (content.has("version")) v = content.get("version").getAsString(); - if (ve == null || !v.equals(veVersion)) { + v = VersionUtilities.getMajMin(v); + if (!ve.containsKey(v)) { if (v.startsWith("5.0")) - ve = new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, null, FhirPublication.R5, true); + ve.put(v, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, null, FhirPublication.R5, true)); else if (v.startsWith("3.0")) - ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, true); + ve.put(v, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, true)); else if (v.startsWith("4.0")) - ve = new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4, true); + ve.put(v, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4, true)); else if (v.startsWith("1.0")) - ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, true); + ve.put(v, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, true)); else throw new Exception("unknown version "+v); - TestingUtilities.fcontext = ve.getContext(); - veVersion = v; } + vCurr = ve.get(v); + TestingUtilities.fcontext = vCurr.getContext(); if (content.has("use-test") && !content.get("use-test").getAsBoolean()) return; String testCaseContent = TestingUtilities.loadTestResource("validator", name.substring(name.indexOf(".")+1)); - InstanceValidator val = ve.getValidator(); + InstanceValidator val = vCurr.getValidator(); val.setDebug(false); if (content.has("allowed-extension-domain")) val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString()); @@ -127,7 +131,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (content.has("questionnaire")) { String filename = content.get("questionnaire").getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - ve.getContext().cacheResource(loadResource(filename, contents, v)); + vCurr.getContext().cacheResource(loadResource(filename, contents, v)); } if (content.has("codesystems")) { for (JsonElement je : content.getAsJsonArray("codesystems")) { @@ -152,7 +156,6 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML); checkOutcomes(errors, content); if (content.has("profile")) { - List errorsProfile = new ArrayList(); JsonObject profile = content.getAsJsonObject("profile"); if (profile.has("supporting")) { for (JsonElement e : profile.getAsJsonArray("supporting")) { @@ -167,12 +170,37 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour System.out.println("Name: " + name+" - profile : "+profile.get("source").getAsString()); v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION; StructureDefinition sd = loadProfile(filename, contents, v, messages); + List errorsProfile = new ArrayList(); if (name.startsWith("Json.")) val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, sd); else val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, sd); checkOutcomes(errorsProfile, profile); - } + } + if (content.has("logical")) { + JsonObject logical = content.getAsJsonObject("logical"); + if (logical.has("supporting")) { + for (JsonElement e : logical.getAsJsonArray("supporting")) { + String filename = e.getAsString(); + String contents = TestingUtilities.loadTestResource("validator", filename); + MetadataResource mr = (MetadataResource) loadResource(filename, contents, v); + if (mr instanceof StructureDefinition) { + val.getContext().generateSnapshot((StructureDefinition) mr, true); + } + val.getContext().cacheResource(mr); + } + } + List errorsLogical = new ArrayList(); + Element le = val.validate(null, errorsLogical, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), (name.startsWith("Json.")) ? FhirFormat.JSON : FhirFormat.XML); + if (logical.has("expressions")) { + FHIRPathEngine fp = new FHIRPathEngine(val.getContext()); + for (JsonElement e : logical.getAsJsonArray("expressions")) { + String exp = e.getAsString(); + Assert.assertTrue(fp.evaluateToBoolean(null, le, le, le, fp.parse(exp))); + } + } + checkOutcomes(errorsLogical, logical); + } } public StructureDefinition loadProfile(String filename, String contents, String v, List messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { @@ -339,7 +367,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour @Override public ValueSet resolveValueSet(Object appContext, String url) { - return ve.getContext().fetchResource(ValueSet.class, url); + return vCurr.getContext().fetchResource(ValueSet.class, url); } }