fix OID translation case

This commit is contained in:
Grahame Grieve 2019-12-13 22:05:49 +11:00
parent 089f539801
commit ba4628eb28
10 changed files with 523 additions and 162 deletions

View File

@ -1098,6 +1098,10 @@ public abstract class BaseWorkerContext implements IWorkerContext {
@Override @Override
public String oid2Uri(String oid) { public String oid2Uri(String oid) {
synchronized (lock) { synchronized (lock) {
if (oid != null && oid.startsWith("urn:oid:")) {
oid = oid.substring(8);
}
String uri = OIDUtils.getUriForOid(oid); String uri = OIDUtils.getUriForOid(oid);
if (uri != null) if (uri != null)
return uri; return uri;

View File

@ -248,6 +248,7 @@ public interface IWorkerContext {
* @throws FHIRException * @throws FHIRException
*/ */
public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException; public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException;
public void generateSnapshot(StructureDefinition mr, boolean ifLogical);
// -- Terminology services ------------------------------------------------------ // -- Terminology services ------------------------------------------------------
@ -543,4 +544,5 @@ public interface IWorkerContext {
public String getLinkForUrl(String corePath, String s); public String getLinkForUrl(String corePath, String s);
} }

View File

@ -593,10 +593,12 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return r; return r;
} }
@Override
public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException { public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException {
generateSnapshot(p, false); generateSnapshot(p, false);
} }
@Override
public void generateSnapshot(StructureDefinition p, boolean logical) throws DefinitionException, FHIRException { public void generateSnapshot(StructureDefinition p, boolean logical) throws DefinitionException, FHIRException {
if (!p.hasSnapshot() && (logical || p.getKind() != StructureDefinitionKind.LOGICAL)) { if (!p.hasSnapshot() && (logical || p.getKind() != StructureDefinitionKind.LOGICAL)) {
if (!p.hasBaseDefinition()) if (!p.hasBaseDefinition())

View File

@ -176,7 +176,7 @@ public class Property {
if (definition.getType().size() > 0) if (definition.getType().size() > 0)
return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode())); return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode()));
else else
return !definition.getPath().contains(".") && structure.getKind() == StructureDefinitionKind.RESOURCE; return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE || structure.getKind() == StructureDefinitionKind.LOGICAL);
} }
public boolean isList() { public boolean isList() {

View File

@ -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;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.formats.IParser.OutputStyle; 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.test.utils.TestingUtilities;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.utilities.cache.PackageCacheManager; import org.hl7.fhir.utilities.cache.PackageCacheManager;
@ -25,158 +27,184 @@ import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import junit.framework.Assert;
public class CDARoundTripTests { public class CDARoundTripTests {
private SimpleWorkerContext context; // private SimpleWorkerContext context;
private FHIRPathEngine fp; // old-test private FHIRPathEngine fp;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
context = new SimpleWorkerContext(); // old-test context = new SimpleWorkerContext();
PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); // old-test PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
context.loadFromPackage(pcm.loadPackage("hl7.fhir.core", "dev"), null, "StructureDefinition"); // old-test context.loadFromPackage(pcm.loadPackage("hl7.fhir.core", "dev"), null, "StructureDefinition");
context.loadFromPackage(pcm.loadPackage("hl7.fhir.cda", "dev"), null, "StructureDefinition"); // old-test context.loadFromPackage(pcm.loadPackage("hl7.fhir.cda", "dev"), null, "StructureDefinition");
fp = new FHIRPathEngine(context); // 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);
//
//// <typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
//// assertEquals("POCD_HD000040", fp.evaluateToString(e, "typeId.extension"));
//// assertEquals("2.16.840.1.113883.1.3", fp.evaluateToString(e, "typeId.root"));
//// <templateId root="2.16.840.1.113883.3.27.1776"/>
//// assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(e, "templateId.root"));
//// <id extension="c266" root="2.16.840.1.113883.19.4"/>
// assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(e, "id.root"));
// assertEquals("c266", fp.evaluateToString(e, "id.extension"));
//
//// <title>Good Health Clinic Consultation Note</title>
// assertEquals("Good Health Clinic Consultation Note", fp.evaluateToString(e, "title.dataString"));
//// <effectiveTime value="20000407"/>
// assertEquals("2000-04-07", fp.evaluateToString(e, "effectiveTime.value"));
//// <confidentialityCode code="N" codeSystem="2.16.840.1.113883.5.25"/>
// assertEquals("N", fp.evaluateToString(e, "confidentialityCode.code"));
// assertEquals("2.16.840.1.113883.5.25", fp.evaluateToString(e, "confidentialityCode.codeSystem"));
//// <languageCode code="en-US"/>
// assertEquals("en-US", fp.evaluateToString(e, "languageCode.code"));
//// <setId extension="BB35" root="2.16.840.1.113883.19.7"/>
// assertEquals("BB35", fp.evaluateToString(e, "setId.extension"));
// assertEquals("2.16.840.1.113883.19.7", fp.evaluateToString(e, "setId.root"));
//// <versionNumber value="2"/>
// assertEquals("2", fp.evaluateToString(e, "versionNumber.value"));
//// <recordTarget>
//// <patientRole>
//// <id extension="12345" root="2.16.840.1.113883.19.5"/>
// assertEquals("12345", fp.evaluateToString(e, "recordTarget.patientRole.id.extension"));
// assertEquals("2.16.840.1.113883.19.5", fp.evaluateToString(e, "recordTarget.patientRole.id.root"));
//// <patient>
//// <name>
//// <family>Levin</family>
// assertEquals("Levin", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.family.dataString"));
//// <given>Henry</given>
// assertEquals("Henry", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.given.dataString"));
//// <suffix>the 7th</suffix>
//// </name>
//// <administrativeGenderCode code="M" codeSystem="2.16.840.1.113883.5.1"/>
//// <birthTime value="19320924"/>
//// </patient>
//// <providerOrganization>
//// <id root="2.16.840.1.113883.19.5"/>
//// </providerOrganization>
//// </patientRole>
//// </recordTarget>
//
//// <component>
//// <structuredBody>
//// <component>
//// <section>
//
//// <component>
//// <section>
//// <code code="8709-8" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
//// <title>Skin Exam</title>
//// <text>Erythematous rash, palmar surface, left index finger.
//// <renderMultiMedia referencedObject="MM1"/>
//// </text>
//
// 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"));
// // <div>Erythematous rash, palmar surface, left index finger.
// // <img src="MM1"/></div>
// 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("<img src=\"MM1\"/>"));
// } 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 @Test
public void testCDA() throws FHIRFormatError, DefinitionException, FileNotFoundException, IOException, FHIRException { public void testSimple() throws IOException {
try { PackageCacheManager pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
SimpleWorkerContext context = SimpleWorkerContext.fromPackage(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1"));
InputStream fileSource = TestingUtilities.loadTestResourceStream("cda", "cda-original.xml"); context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "any.xml"), "any.xml", null);
String roundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.xml"); context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ii.xml"), "ii.xml", null);
String jsonRoundTrip = TestingUtilities.tempFile("cda", "cda-roundtrip.json"); context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cd.xml"), "cd.xml", null);
context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "ce.xml"), "ce.xml", null);
Element e = Manager.parse(context, fileSource, FhirFormat.XML); context.loadFromFile(TestingUtilities.loadTestResourceStream("r5", "cda", "cda.xml"), "cda.xml", null);
for (StructureDefinition sd : context.getStructures()) {
Manager.compose(context, e, new FileOutputStream(roundTrip), FhirFormat.XML, OutputStyle.PRETTY, null); if (!sd.hasSnapshot()) {
Manager.compose(context, e, new FileOutputStream(jsonRoundTrip), FhirFormat.JSON, OutputStyle.PRETTY, null); System.out.println("generate snapshot for "+sd.getUrl());
context.generateSnapshot(sd, true);
// <typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/> }
// assertEquals("POCD_HD000040", fp.evaluateToString(e, "typeId.extension"));
// assertEquals("2.16.840.1.113883.1.3", fp.evaluateToString(e, "typeId.root"));
// <templateId root="2.16.840.1.113883.3.27.1776"/>
// assertEquals("2.16.840.1.113883.3.27.1776", fp.evaluateToString(e, "templateId.root"));
// <id extension="c266" root="2.16.840.1.113883.19.4"/>
assertEquals("2.16.840.1.113883.19.4", fp.evaluateToString(e, "id.root"));
assertEquals("c266", fp.evaluateToString(e, "id.extension"));
// <title>Good Health Clinic Consultation Note</title>
assertEquals("Good Health Clinic Consultation Note", fp.evaluateToString(e, "title.dataString"));
// <effectiveTime value="20000407"/>
assertEquals("2000-04-07", fp.evaluateToString(e, "effectiveTime.value"));
// <confidentialityCode code="N" codeSystem="2.16.840.1.113883.5.25"/>
assertEquals("N", fp.evaluateToString(e, "confidentialityCode.code"));
assertEquals("2.16.840.1.113883.5.25", fp.evaluateToString(e, "confidentialityCode.codeSystem"));
// <languageCode code="en-US"/>
assertEquals("en-US", fp.evaluateToString(e, "languageCode.code"));
// <setId extension="BB35" root="2.16.840.1.113883.19.7"/>
assertEquals("BB35", fp.evaluateToString(e, "setId.extension"));
assertEquals("2.16.840.1.113883.19.7", fp.evaluateToString(e, "setId.root"));
// <versionNumber value="2"/>
assertEquals("2", fp.evaluateToString(e, "versionNumber.value"));
// <recordTarget>
// <patientRole>
// <id extension="12345" root="2.16.840.1.113883.19.5"/>
assertEquals("12345", fp.evaluateToString(e, "recordTarget.patientRole.id.extension"));
assertEquals("2.16.840.1.113883.19.5", fp.evaluateToString(e, "recordTarget.patientRole.id.root"));
// <patient>
// <name>
// <family>Levin</family>
assertEquals("Levin", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.family.dataString"));
// <given>Henry</given>
assertEquals("Henry", fp.evaluateToString(e, "recordTarget.patientRole.patient.name.given.dataString"));
// <suffix>the 7th</suffix>
// </name>
// <administrativeGenderCode code="M" codeSystem="2.16.840.1.113883.5.1"/>
// <birthTime value="19320924"/>
// </patient>
// <providerOrganization>
// <id root="2.16.840.1.113883.19.5"/>
// </providerOrganization>
// </patientRole>
// </recordTarget>
// <component>
// <structuredBody>
// <component>
// <section>
// <component>
// <section>
// <code code="8709-8" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
// <title>Skin Exam</title>
// <text>Erythematous rash, palmar surface, left index finger.
// <renderMultiMedia referencedObject="MM1"/>
// </text>
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"));
// <div>Erythematous rash, palmar surface, left index finger.
// <img src="MM1"/></div>
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("<img src=\"MM1\"/>"));
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
throw e;
} }
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);
}
} }

View File

@ -31,6 +31,9 @@ public class OIDUtils {
*/ */
public static String getUriForOid(String r) { public static String getUriForOid(String r) {
if (r == null) {
return null;
}
if (r.equals("2.16.840.1.113883.6.96")) if (r.equals("2.16.840.1.113883.6.96"))
return "http://snomed.info/sct"; return "http://snomed.info/sct";
if (r.equals("2.16.840.1.113883.6.1")) if (r.equals("2.16.840.1.113883.6.1"))

View File

@ -135,7 +135,10 @@ public class VersionUtilities {
if (version == null) if (version == null)
return 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("\\."); String[] p = version.split("\\.");
return p[0]+"."+p[1]; return p[0]+"."+p[1];
} else { } else {

View File

@ -31,7 +31,7 @@ public class ValidationOptions {
public boolean isGuessSystem() { public boolean isGuessSystem() {
return guessSystem; return guessSystem;
} }
private ValidationOptions copy() { private ValidationOptions copy() {
ValidationOptions n = new ValidationOptions(language); ValidationOptions n = new ValidationOptions(language);
n.useServer = useServer; n.useServer = useServer;

View File

@ -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.DiscriminatorType;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; 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.ElementDefinitionSlicingDiscriminatorComponent;
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 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.StringType;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 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.StructureDefinitionSnapshotComponent;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.TimeType; 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 InstanceValidator extends BaseValidator implements IResourceValidator {
public class ValidatorHostContext { public class ValidatorHostContext {
private Object appContext; private Object appContext;
private Element container; // bundle, or parameters private Element container; // bundle, or parameters
@ -1135,6 +1138,245 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return res; return res;
} }
private boolean checkTerminologyCodeableConcept(List<ValidationMessage> 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<ValidationMessage> 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<String> maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed);
for (String m : maps) {
String name = tail(ed.getPath());
List<Element> 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<String> maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed);
for (String m : maps) {
String name = tail(ed.getPath());
List<Element> 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<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
@ -4194,7 +4436,18 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
} else if (type.equals("Resource")) { } else if (type.equals("Resource")) {
validateContains(hostContext, errors, ei.path, ei.definition, definition, resource, ei.element, localStack, idStatusForEntry(element, ei)); // if validateContains(hostContext, errors, ei.path, ei.definition, definition, resource, ei.element, localStack, idStatusForEntry(element, ei)); // if
// (str.matches(".*([.,/])work\\1$")) // (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<String> 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 { } else {
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.name)) 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); 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<String> getMapping(String url, StructureDefinition defn, ElementDefinition elem) {
List<String> 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) { public void checkMustSupport(StructureDefinition profile, ElementInfo ei) {
String usesMustSupport = profile.getUserString("usesMustSupport"); String usesMustSupport = profile.getUserString("usesMustSupport");
if (usesMustSupport == null) { if (usesMustSupport == null) {

View File

@ -23,11 +23,14 @@ import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; 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;
import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy; import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy;
import org.hl7.fhir.r5.validation.InstanceValidator; import org.hl7.fhir.r5.validation.InstanceValidator;
import org.hl7.fhir.r5.validation.ValidationEngine; 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;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.validation.tests.utilities.TestUtilities; import org.hl7.fhir.validation.tests.utilities.TestUtilities;
@ -72,7 +75,6 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
private String name; private String name;
private JsonObject content; private JsonObject content;
private static String veVersion;
public ValidationTestSuite(String name, JsonObject content) { public ValidationTestSuite(String name, JsonObject content) {
this.name = name; 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://tx.fhir.org";
// private static final String DEF_TX = "http://local.fhir.org:960"; // private static final String DEF_TX = "http://local.fhir.org:960";
private static ValidationEngine ve; private static Map<String, ValidationEngine> ve = new HashMap<>();
private static ValidationEngine vCurr;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
@ -92,27 +95,28 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
if (content.has("version")) if (content.has("version"))
v = content.get("version").getAsString(); v = content.get("version").getAsString();
if (ve == null || !v.equals(veVersion)) { v = VersionUtilities.getMajMin(v);
if (!ve.containsKey(v)) {
if (v.startsWith("5.0")) 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")) 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")) 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")) 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 else
throw new Exception("unknown version "+v); 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()) if (content.has("use-test") && !content.get("use-test").getAsBoolean())
return; return;
String testCaseContent = TestingUtilities.loadTestResource("validator", name.substring(name.indexOf(".")+1)); String testCaseContent = TestingUtilities.loadTestResource("validator", name.substring(name.indexOf(".")+1));
InstanceValidator val = ve.getValidator(); InstanceValidator val = vCurr.getValidator();
val.setDebug(false); val.setDebug(false);
if (content.has("allowed-extension-domain")) if (content.has("allowed-extension-domain"))
val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString()); val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString());
@ -127,7 +131,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
if (content.has("questionnaire")) { if (content.has("questionnaire")) {
String filename = content.get("questionnaire").getAsString(); String filename = content.get("questionnaire").getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename); String contents = TestingUtilities.loadTestResource("validator", filename);
ve.getContext().cacheResource(loadResource(filename, contents, v)); vCurr.getContext().cacheResource(loadResource(filename, contents, v));
} }
if (content.has("codesystems")) { if (content.has("codesystems")) {
for (JsonElement je : content.getAsJsonArray("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); val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML);
checkOutcomes(errors, content); checkOutcomes(errors, content);
if (content.has("profile")) { if (content.has("profile")) {
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
JsonObject profile = content.getAsJsonObject("profile"); JsonObject profile = content.getAsJsonObject("profile");
if (profile.has("supporting")) { if (profile.has("supporting")) {
for (JsonElement e : profile.getAsJsonArray("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()); System.out.println("Name: " + name+" - profile : "+profile.get("source").getAsString());
v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION; v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION;
StructureDefinition sd = loadProfile(filename, contents, v, messages); StructureDefinition sd = loadProfile(filename, contents, v, messages);
List<ValidationMessage> errorsProfile = new ArrayList<ValidationMessage>();
if (name.startsWith("Json.")) if (name.startsWith("Json."))
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, sd); val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON, sd);
else else
val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, sd); val.validate(null, errorsProfile, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.XML, sd);
checkOutcomes(errorsProfile, profile); 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<ValidationMessage> errorsLogical = new ArrayList<ValidationMessage>();
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<ValidationMessage> messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { public StructureDefinition loadProfile(String filename, String contents, String v, List<ValidationMessage> messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
@ -339,7 +367,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
@Override @Override
public ValueSet resolveValueSet(Object appContext, String url) { public ValueSet resolveValueSet(Object appContext, String url) {
return ve.getContext().fetchResource(ValueSet.class, url); return vCurr.getContext().fetchResource(ValueSet.class, url);
} }
} }