diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java index 5cc02f17c..d33aba8ee 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java @@ -32,6 +32,8 @@ package org.hl7.fhir.r5.conformance; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -63,9 +65,10 @@ public class ShExGenerator { public ConstraintTranslationPolicy constraintPolicy = ConstraintTranslationPolicy.ALL; - private static String SHEX_TEMPLATE = "$header$\n\n" + - - "$shapeDefinitions$"; + private static String SHEX_TEMPLATE = + "$header$\n" + + "$imports$\n" + + "$shapeDefinitions$"; // A header is a list of prefixes, a base declaration and a start node private static String FHIR = "http://hl7.org/fhir/"; @@ -75,7 +78,9 @@ public class ShExGenerator { "PREFIX fhirvs: <$fhirvs$>\n" + "PREFIX xsd: \n" + "PREFIX rdf: \n" + - "BASE \n$start$"; + "BASE \n"; + + private static String IMPORT_TEMPLATE = "IMPORT <$import$$fileExt$>\n"; // Start template for single (open) entry private static String START_TEMPLATE = "\n\nstart=@<$id$> AND {fhir:nodeRole [fhir:treeRoot]}\n"; @@ -257,6 +262,8 @@ public class ShExGenerator { // Extensions are Structure Definitions with type as "Extension". private List selectedExtensions; private List selectedExtensionUrls; + + private List imports; private FHIRPathEngine fpe; public ShExGenerator(IWorkerContext context) { @@ -276,6 +283,7 @@ public class ShExGenerator { excludedSDUrls = new ArrayList(); selectedExtensions = new ArrayList(); selectedExtensionUrls = new ArrayList(); + imports = new ArrayList(); fpe = new FHIRPathEngine(context); } @@ -293,6 +301,7 @@ public class ShExGenerator { references.clear(); required_value_sets.clear(); known_resources.clear(); + imports.clear(); return generate(links, list); } @@ -369,7 +378,6 @@ public class ShExGenerator { shex_def.add("header", tmplt(HEADER_TEMPLATE). - add("start", start_cmd). add("fhir", FHIR). add("fhirvs", FHIR_VS).render()); @@ -380,6 +388,7 @@ public class ShExGenerator { // We remove them. Also, it is possible for the same sd to have multiple hashes... uniq_structures = new LinkedList(); uniq_structure_urls = new HashSet(); + StringBuffer allStructures = new StringBuffer(""); for (StructureDefinition sd : structures) { // Exclusion Criteria... if ((excludedSDUrls != null) && @@ -415,79 +424,89 @@ public class ShExGenerator { } } - boolean isShapeDefinitionEmpty = true; for (StructureDefinition sd : uniq_structures) { printBuildMessage(" ---- Generating ShEx for : " + sd.getName() + " [ " + sd.getUrl() + " ] ..."); String shapeDefinitionStr = genShapeDefinition(sd, true); if (!shapeDefinitionStr.isEmpty()) { - isShapeDefinitionEmpty = false; shapeDefinitions.append(shapeDefinitionStr); + } else { + printBuildMessage(" ---- WARNING! EMPTY/No ShEx SCHEMA Body generated for : " + sd.getName() + " [ " + sd.getUrl() + " ].\n" + + "This might not be an issue, if this resource is normative base or a meta resource"); + shapeDefinitions.append("<" + sd.getName() + "> CLOSED {\n}"); } - else { - printBuildMessage(" ---- EMPTY/No ShEx SCHEMA generated for : " + sd.getName() + " [ " + sd.getUrl() + " ]."); + + if (!imports.isEmpty()) + imports.removeIf(s -> s.contains(sd.getName())); + } + shapeDefinitions.append(emitInnerTypes()); + + // If data types are to be put in the same file + if (doDatatypes) { + shapeDefinitions.append("\n#---------------------- Data Types -------------------\n"); + while (emittedDatatypes.size() < datatypes.size() || + emittedInnerTypes.size() < innerTypes.size()) { + shapeDefinitions.append(emitDataTypes()); + // As process data types, it may introduce some more inner types, so we repeat the call here. + shapeDefinitions.append(emitInnerTypes()); + } } - } - // There was not shape generated. return empty. - // No need to generate data types, references and valuesets - if (isShapeDefinitionEmpty) { - return ""; - } - - shapeDefinitions.append(emitInnerTypes()); - - // If data types are to be put in the same file - if(doDatatypes) { - shapeDefinitions.append("\n#---------------------- Data Types -------------------\n"); - while (emittedDatatypes.size() < datatypes.size() || - emittedInnerTypes.size() < innerTypes.size()) { - shapeDefinitions.append(emitDataTypes()); - // As process data types, it may introduce some more inner types, so we repeat the call here. - shapeDefinitions.append(emitInnerTypes()); + if (oneOrMoreTypes.size() > 0) { + shapeDefinitions.append("\n#---------------------- Cardinality Types (OneOrMore) -------------------\n"); + oneOrMoreTypes.forEach((String oomType) -> { + shapeDefinitions.append(getOneOrMoreType(oomType)); + }); } - } - if (oneOrMoreTypes.size() > 0) { - shapeDefinitions.append("\n#---------------------- Cardinality Types (OneOrMore) -------------------\n"); - oneOrMoreTypes.forEach((String oomType) -> { - shapeDefinitions.append(getOneOrMoreType(oomType)); + if (references.size() > 0) { + shapeDefinitions.append("\n#---------------------- Reference Types -------------------\n"); + for (String r : references) { + shapeDefinitions.append("\n").append(tmplt(TYPED_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n"); + if (!"Resource".equals(r) && !known_resources.contains(r)) + shapeDefinitions.append("\n").append(tmplt(TARGET_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n"); + } + } + + if (completeModel && known_resources.size() > 0) { + shapeDefinitions.append("\n").append(tmplt(COMPLETE_RESOURCE_TEMPLATE) + .add("resources", StringUtils.join(known_resources, "> OR\n\t@<")).render()); + List all_entries = new ArrayList(); + for (String kr : known_resources) + all_entries.add(tmplt(ALL_ENTRY_TEMPLATE).add("id", kr).render()); + shapeDefinitions.append("\n").append(tmplt(ALL_TEMPLATE) + .add("all_entries", StringUtils.join(all_entries, " OR\n\t")).render()); + } + + if (required_value_sets.size() > 0) { + shapeDefinitions.append("\n#---------------------- Value Sets ------------------------\n"); + for (ValueSet vs : required_value_sets) + shapeDefinitions.append("\n").append(genValueSet(vs)); + } + + if ((unMappedFunctions != null) && (!unMappedFunctions.isEmpty())) { + debug("------------------------- Unmapped Functions ---------------------"); + for (String um : unMappedFunctions) { + debug(um); + } + } + + allStructures.append(shapeDefinitions + "\n"); + + StringBuffer allImports = new StringBuffer(""); + if (!imports.isEmpty()) { + imports.sort(Comparator.comparingInt(String::length)); + imports.forEach((String imp) -> { + ST import_def = tmplt(IMPORT_TEMPLATE); + import_def.add("import", imp); + import_def.add("fileExt", ".shex"); + allImports.append(import_def.render()); }); } - if (references.size() > 0) { - shapeDefinitions.append("\n#---------------------- Reference Types -------------------\n"); - for (String r : references) { - shapeDefinitions.append("\n").append(tmplt(TYPED_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n"); - if (!"Resource".equals(r) && !known_resources.contains(r)) - shapeDefinitions.append("\n").append(tmplt(TARGET_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n"); - } - } - - shex_def.add("shapeDefinitions", shapeDefinitions); - - if(completeModel && known_resources.size() > 0) { - shapeDefinitions.append("\n").append(tmplt(COMPLETE_RESOURCE_TEMPLATE) - .add("resources", StringUtils.join(known_resources, "> OR\n\t@<")).render()); - List all_entries = new ArrayList(); - for(String kr: known_resources) - all_entries.add(tmplt(ALL_ENTRY_TEMPLATE).add("id", kr).render()); - shapeDefinitions.append("\n").append(tmplt(ALL_TEMPLATE) - .add("all_entries", StringUtils.join(all_entries, " OR\n\t")).render()); - } - - if (required_value_sets.size() > 0) { - shapeDefinitions.append("\n#---------------------- Value Sets ------------------------\n"); - for (ValueSet vs : required_value_sets) - shapeDefinitions.append("\n").append(genValueSet(vs)); - } - - if ((unMappedFunctions != null)&&(!unMappedFunctions.isEmpty())) { - debug("------------------------- Unmapped Functions ---------------------"); - for (String um : unMappedFunctions) { - debug(um); - } - } + allImports.append(start_cmd); + shex_def.add("imports", allImports); + shex_def.add("shapeDefinitions", allStructures.toString()); return shex_def.render(); } @@ -502,7 +521,7 @@ public class ShExGenerator { bd = sd.getBaseDefinition(); String[] els = bd.split("/"); bd = els[els.length - 1]; - + addImport("<" + bd + ">"); sId += "> EXTENDS @<" + bd; } @@ -514,6 +533,7 @@ public class ShExGenerator { return ""; String bd = (ed.getType().size() > 0)? (ed.getType().get(0).getCode()) : ""; if (bd != null && !bd.isEmpty() && !baseDataTypes.contains(bd)) { + addImport("<" + bd + ">"); bd = "> EXTENDS @<" + bd; } return bd; @@ -661,14 +681,14 @@ public class ShExGenerator { String[] backRefs = toStore.split("\\."); toStore = "a [fhir:" + backRefs[0] + "]"; for (int i = 1; i < backRefs.length; i++) - toStore = "( ^fhir:" + backRefs[i] + " {" + toStore + "} )"; + toStore = "^fhir:" + backRefs[i] + " {" + toStore + "}"; if (!contextOfUse.contains(toStore)) { contextOfUse.add(toStore); } } } - contextOfUseStr = "^fhir:extension { " + StringUtils.join(contextOfUse, " OR \n\t\t\t\t") + "\n\t\t}"; + contextOfUseStr = "^fhir:extension { " + StringUtils.join(contextOfUse, " OR \n ") + "\n }"; } shape_defn.add("contextOfUse", contextOfUseStr); @@ -684,7 +704,7 @@ public class ShExGenerator { private String translateConstraint(StructureDefinition sd, ElementDefinition ed, ElementDefinition.ElementDefinitionConstraintComponent constraint){ String translated = ""; - if (constraint != null) { + if (false) { String ce = constraint.getExpression(); String constItem = "FHIR-SD-Path:" + ed.getPath() + " Expression: " + ce; try { @@ -700,11 +720,9 @@ public class ShExGenerator { debug(" TRANSLATED\t"+ed.getPath()+"\t"+constraint.getHuman()+"\t"+constraint.getExpression()+"\t"+shexConstraint); } catch (Exception e) { - String message = " FAILED to parse the constraint: " + constItem + " [ " + e.getMessage() + " ]"; - // Now make this a comment so that it does not fail when schema is resolved in validator - // TODO: This needs to be fixed - // TODO: it should be - // translated = message + //String message = " FAILED to parse the constraint from Structure Definition: " + constItem + " [ " + e.getMessage() + " ]"; + String message = " FAILED to parse the constraint from Structure Definition: " + constItem; + e.printStackTrace(); translated = ""; debug(message); @@ -1320,12 +1338,31 @@ public class ShExGenerator { } element_def.add("defn", defn); + addImport(defn); element_def.add("card", card); addComment(element_def, ed); return element_def.render(); } + private void addImport(String typeDefn) { + if ((typeDefn != null) && (!typeDefn.isEmpty())) { + // String importType = StringUtils.substringBetween(typeDefn, "<", ">"); + // if ((importType.indexOf(ONE_OR_MORE_PREFIX) == -1) && + // (!imports.contains(importType))) + // imports.add(importType); + // } + Pattern p = Pattern.compile("<([^\\s>/]+)"); + Matcher m = p.matcher(typeDefn); + while (m.find()) { + String tag = m.group(1); + //System.out.println("FOUND IMPORT: " + tag); + if ((tag.indexOf(ONE_OR_MORE_PREFIX) == -1) && + (!imports.contains(tag))) + imports.add(tag); + } + } + } private List getChildren(StructureDefinition derived, ElementDefinition element) { List elements = derived.getSnapshot().getElement(); int index = elements.indexOf(element) + 1; @@ -1573,7 +1610,9 @@ public class ShExGenerator { // shex_choice_entry.add("id", "fhir:" + base+Character.toUpperCase(ext.charAt(0)) + ext.substring(1) + " "); shex_choice_entry.add("id", ""); shex_choice_entry.add("card", ""); - shex_choice_entry.add("defn", genTypeRef(sd, ed, id, typ)); + String typeDefn = genTypeRef(sd, ed, id, typ); + shex_choice_entry.add("defn", typeDefn); + addImport(typeDefn); shex_choice_entry.add("comment", " "); return shex_choice_entry.render(); } @@ -1604,6 +1643,8 @@ public class ShExGenerator { one_or_more_type.add("oomType", oomType); one_or_more_type.add("origType", origType); one_or_more_type.add("restriction", restriction); + addImport(origType); + addImport(restriction); one_or_more_type.add("comment", ""); return one_or_more_type.render(); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java index ee490b294..2b6d52584 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java @@ -20,21 +20,23 @@ public class ShexGeneratorTestUtils { public String name; public String url; public String info; + public RESOURCE_CATEGORY kind; - public resDef(String _name, String _url, String _info){ + public resDef(String _name, String _url, String _info, RESOURCE_CATEGORY _kind){ this.name = _name; this.url = _url; this.info = _info; + this.kind = _kind; } @Override public String toString() { - return " " + name + "[ " + url + " ] "; + return " " + name + " (Kind: " + kind + " ) [ " + url + " ]"; } } public enum RESOURCE_CATEGORY{ - LOGICAL_NAMES, STRUCTURE_DEFINITIONS, EXTENSIONS, PROFILES, ALL + LOGICAL_NAME, STRUCTURE_DEFINITION, EXTENSION, PROFILE, ALL, META_OR_EXAMPLE_OR_OTHER_IGNORE } /** @@ -48,26 +50,25 @@ public class ShexGeneratorTestUtils { List selSDs = new ArrayList(); sds.forEach((StructureDefinition sd) -> { switch(cat) { - case STRUCTURE_DEFINITIONS: - if (sd.getType().trim().equals(sd.getName().trim())) - selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd))); + case STRUCTURE_DEFINITION: + if (getCategory(sd).equals(RESOURCE_CATEGORY.STRUCTURE_DEFINITION)) + selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd), RESOURCE_CATEGORY.STRUCTURE_DEFINITION)); break; - case LOGICAL_NAMES: - if (sd.getBaseDefinition() == null) - selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd))); + case LOGICAL_NAME: + if (getCategory(sd).equals(RESOURCE_CATEGORY.LOGICAL_NAME)) + selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd), RESOURCE_CATEGORY.LOGICAL_NAME)); break; - case EXTENSIONS: - if ("Extension".equals(sd.getType())) - selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd))); + case EXTENSION: + if (getCategory(sd).equals(RESOURCE_CATEGORY.EXTENSION)) + selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd), RESOURCE_CATEGORY.EXTENSION)); break; - case PROFILES: - if (!((sd.getBaseDefinition() == null) || - ("Extension".equals(sd.getType())) || - (sd.getType().trim().equals(sd.getName().trim())))) - selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd))); + case PROFILE: + if (getCategory(sd).equals(RESOURCE_CATEGORY.PROFILE)) + selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd), RESOURCE_CATEGORY.PROFILE)); break; default: - selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd))); + selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd), getCategory(sd))); + } }); @@ -81,6 +82,24 @@ public class ShexGeneratorTestUtils { return selSDs; } + private RESOURCE_CATEGORY getCategory(StructureDefinition sd) { + if ("Extension".equals(sd.getType())) + return RESOURCE_CATEGORY.EXTENSION; + + if (sd.getBaseDefinition() == null) + return RESOURCE_CATEGORY.LOGICAL_NAME; + + if (sd.getType().trim().equals(sd.getName().trim())) + return RESOURCE_CATEGORY.STRUCTURE_DEFINITION; + + if (!((sd.getBaseDefinition() == null) || + ("Extension".equals(sd.getType())) || + (sd.getType().trim().equals(sd.getName().trim())))) + return RESOURCE_CATEGORY.PROFILE; + + return RESOURCE_CATEGORY.META_OR_EXAMPLE_OR_OTHER_IGNORE; + } + /** * This method is used in testing only - during Resource Constraint translation to ShEx constructs. * It can be used by future developers to avoid unnecessary processing of constraints @@ -196,7 +215,7 @@ public class ShexGeneratorTestUtils { System.out.println("Printing " + title); System.out.println("************************************************************************"); items.forEach((resDef item) -> { - System.out.println(item.name + " [" + item.url + "]"); + System.out.println(item.name + " \t[" + item.url + "]\t" + item.info); }); } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java index 0fae2f615..d7f0eb0b7 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java @@ -10,7 +10,6 @@ import java.util.List; import es.weso.shex.Schema; import es.weso.shex.validator.ShExsValidator; import es.weso.shex.validator.ShExsValidatorBuilder; -import org.apache.commons.lang3.StringUtils; import org.fhir.ucum.UcumException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; @@ -36,92 +35,92 @@ public class ShexGeneratorTests { public static void setup() { } - private void doTest(String name) throws FileNotFoundException, IOException, FHIRException, UcumException { - StructureDefinition sd = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); - if (sd == null) { - throw new FHIRException("StructuredDefinition for " + name + "was null"); - } - Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex"); - TextFile.stringToFile(new ShExGenerator(TestingUtilities.getSharedWorkerContext()).generate(HTMLLinkPolicy.NONE, sd), outPath.toString()); + private void doTest(String name, ShexGeneratorTestUtils.RESOURCE_CATEGORY cat) throws FileNotFoundException, IOException, FHIRException, UcumException { +// StructureDefinition sd = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); +// if (sd == null) { +// throw new FHIRException("StructuredDefinition for " + name + "was null"); +// } +// Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex"); +// TextFile.stringToFile(new ShExGenerator(TestingUtilities.getSharedWorkerContext()).generate(HTMLLinkPolicy.NONE, sd), outPath.toString()); // For Testing Schema Processing and Constraint Mapping related Development // If you un-comment the following lines, please comment all other lines in this method. - //this.doTestThis(name.toLowerCase(), name, false, ShExGenerator.ConstraintTranslationPolicy.ALL, true, true); + this.doTestSingleSD(name.toLowerCase(), cat, name, false, ShExGenerator.ConstraintTranslationPolicy.ALL, true, true, false); } @Test public void testId() throws FHIRException, IOException, UcumException { - doTest("id"); + doTest("id", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testUri() throws FHIRException, IOException, UcumException { - doTest("uri"); + doTest("uri", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testObservation() throws FHIRException, IOException, UcumException { - doTest("Observation"); + doTest("Observation", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testRef() throws FHIRException, IOException, UcumException { - doTest("Reference"); + doTest("Reference", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testAccount() throws FHIRException, IOException, UcumException { - doTest("Account"); + doTest("Account", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testAppointment() throws FHIRException, IOException, UcumException { - doTest("Appointment"); + doTest("Appointment", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testBundle() throws FHIRException, IOException, UcumException { - doTest("Bundle"); + doTest("Bundle", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testAge() throws FHIRException, IOException, UcumException { - doTest("Age"); + doTest("Age", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testMedicationRequest() throws FHIRException, IOException, UcumException { - doTest("MedicationRequest"); + doTest("MedicationRequest", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testAllergyIntolerance() throws FHIRException, IOException, UcumException { - doTest("AllergyIntolerance"); + doTest("AllergyIntolerance", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testCoding() throws FHIRException, IOException, UcumException { - doTest("Coding"); + doTest("Coding", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testTiming() throws FHIRException, IOException, UcumException { - doTest("Timing"); + doTest("Timing", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Test public void testSignature() throws FHIRException, IOException, UcumException { - doTest("Signature"); + doTest("Signature", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } @Ignore public void testCapabilityStatement() throws FHIRException, IOException, UcumException { - doTest("CapabilityStatement"); + doTest("CapabilityStatement", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } - private void doTestThis(String shortName, String name, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx) { + private void doTestSingleSD(String shortName, ShexGeneratorTestUtils.RESOURCE_CATEGORY cat, String name, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx, boolean excludeMetaSDs) { IWorkerContext ctx = TestingUtilities.getSharedWorkerContext(); StructureDefinition sd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); if (sd == null) { - throw new FHIRException("StructuredDefinition for " + name + " was null"); + throw new FHIRException("StructuredDefinition for " + name + "(Kind:" + cat + ") was null"); } //Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex"); Path outPath = FileSystems.getDefault().getPath(System.getProperty("user.home") + "/runtime_environments/ShExSchemas", shortName + ".shex"); @@ -131,9 +130,11 @@ public class ShexGeneratorTests { this.shexGenerator.debugMode = debugMode; this.shexGenerator.constraintPolicy = policy; - // ShEx Generator skips resources which are at Meta level of FHIR Resource definitions - this.shexGenerator.setExcludedStructureDefinitionUrls( - ShexGeneratorTestUtils.getMetaStructureDefinitionsToSkip()); + if (excludeMetaSDs) { + // ShEx Generator skips resources which are at Meta level of FHIR Resource definitions + this.shexGenerator.setExcludedStructureDefinitionUrls( + ShexGeneratorTestUtils.getMetaStructureDefinitionsToSkip()); + } // when ShEx translates only selected resource extensions if (useSelectedExtensions) { @@ -155,10 +156,10 @@ public class ShexGeneratorTests { Schema sch = validator.schema(); Assert.assertNotNull(sch); - System.out.println("VALIDATION PASSED for ShEx Schema " + sd.getName()); + System.out.println("VALIDATION PASSED for ShEx Schema " + sd.getName() + " (Kind:" + cat + ")" ); } catch (Exception e) { - System.out.println("VALIDATION FAILED for ShEx Schema " + sd.getName()); - //System.out.println("\t\t\tMessage: " + e.getMessage()); + System.out.println("VALIDATION FAILED for ShEx Schema " + sd.getName() + " (Kind:" + cat + ")" ); + e.printStackTrace(); } } TextFile.stringToFile(schema, outPath.toString()); @@ -168,7 +169,55 @@ public class ShexGeneratorTests { } } - @Ignore + private void doTestBatchSD(List sds, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx, boolean excludeMetaSDs) { + IWorkerContext ctx = TestingUtilities.getSharedWorkerContext(); + //Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex"); + Path outPath = FileSystems.getDefault().getPath(System.getProperty("user.home") + "/runtime_environments/ShExSchemas", "ShEx.shex"); + try { + this.shexGenerator = new ShExGenerator(ctx); + + this.shexGenerator.debugMode = debugMode; + this.shexGenerator.constraintPolicy = policy; + + if (excludeMetaSDs) { + // ShEx Generator skips resources which are at Meta level of FHIR Resource definitions + this.shexGenerator.setExcludedStructureDefinitionUrls( + ShexGeneratorTestUtils.getMetaStructureDefinitionsToSkip()); + } + + // when ShEx translates only selected resource extensions + if (useSelectedExtensions) { + List selExtns = new ArrayList(); + for (String eUrl : ShexGeneratorTestUtils.getSelectedExtensions()) { + StructureDefinition esd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(eUrl, null)); + if (esd != null) + selExtns.add(esd); + } + this.shexGenerator.setSelectedExtension(selExtns); + } + + String schema = this.shexGenerator.generate(HTMLLinkPolicy.NONE, sds); + if (!schema.isEmpty()) { + if (validateShEx) { + try { + ShExsValidator validator = ShExsValidatorBuilder.fromStringSync(schema, "ShexC"); + Schema sch = validator.schema(); + + Assert.assertNotNull(sch); + System.out.println("VALIDATION PASSED for ShEx Schema ALL SHEX STRUCTURES"); + } catch (Exception e) { + System.out.println("VALIDATION FAILED for ShEx Schema ALL SHEX STRUCTURES"); + e.printStackTrace(); + } + } + TextFile.stringToFile(schema, outPath.toString()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test public void doTestAll() throws FileNotFoundException, IOException, FHIRException, UcumException { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); @@ -176,8 +225,9 @@ public class ShexGeneratorTests { ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process all extensions - ShExGenerator.ConstraintTranslationPolicy.ALL + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all types of constraints, do not skip + true ); } @@ -189,8 +239,9 @@ public class ShexGeneratorTests { ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process all extensions - ShExGenerator.ConstraintTranslationPolicy.GENERIC_ONLY + ShExGenerator.ConstraintTranslationPolicy.GENERIC_ONLY, // Process generic constraints only, ignore constraints of type 'context of use' + false ); } @@ -203,8 +254,9 @@ public class ShexGeneratorTests { ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process all extensions - ShExGenerator.ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY + ShExGenerator.ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY, // Process constraints only where context of use found, skip otherwise + false ); } @@ -216,7 +268,8 @@ public class ShexGeneratorTests { ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions true, //Process only given/selected extensions, ignore other extensions - ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints + false ); } @@ -225,10 +278,11 @@ public class ShexGeneratorTests { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); processSDList( - ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITIONS, // Processing All kinds of Structure Definitions + ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions - ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints + false ); } @@ -237,10 +291,11 @@ public class ShexGeneratorTests { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); processSDList( - ShexGeneratorTestUtils.RESOURCE_CATEGORY.EXTENSIONS, // Processing All kinds of Structure Definitions + ShexGeneratorTestUtils.RESOURCE_CATEGORY.EXTENSION, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions - ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints + false ); } @@ -249,10 +304,11 @@ public class ShexGeneratorTests { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); processSDList( - ShexGeneratorTestUtils.RESOURCE_CATEGORY.LOGICAL_NAMES, // Processing All kinds of Structure Definitions + ShexGeneratorTestUtils.RESOURCE_CATEGORY.LOGICAL_NAME, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions - ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints + false ); } @@ -260,17 +316,19 @@ public class ShexGeneratorTests { public void testProfilesOnly() throws FileNotFoundException, IOException, FHIRException, UcumException { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); processSDList( - ShexGeneratorTestUtils.RESOURCE_CATEGORY.PROFILES, // Processing All kinds of Structure Definitions + ShexGeneratorTestUtils.RESOURCE_CATEGORY.PROFILE, // Processing All kinds of Structure Definitions sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions - ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints + ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints + false ); } private void processSDList(ShexGeneratorTestUtils.RESOURCE_CATEGORY cat, List sds, boolean useSelectedExtensions, - ShExGenerator.ConstraintTranslationPolicy policy) { + ShExGenerator.ConstraintTranslationPolicy policy, + boolean batchMode) { if ((sds == null) || (sds.isEmpty())) { throw new FHIRException("No StructuredDefinition found!"); } @@ -283,15 +341,20 @@ public class ShexGeneratorTests { System.out.println("Processing " + cat); System.out.println("************************************************************************"); - sdDefs.forEach((ShexGeneratorTestUtils.resDef resDef) -> { - String name = resDef.url; - if (resDef.url.indexOf("/") != -1) { - String els[] = resDef.url.split("/"); - name = els[els.length - 1]; - } - System.out.println("******************** " + resDef + " *********************"); - doTestThis(name, resDef.url, useSelectedExtensions, policy, true, true); - }); + if (!batchMode) { + sdDefs.forEach((ShexGeneratorTestUtils.resDef resDef) -> { + String name = resDef.url; + if (resDef.url.indexOf("/") != -1) { + String els[] = resDef.url.split("/"); + name = els[els.length - 1]; + } + System.out.println("******************** " + resDef + " *********************"); + doTestSingleSD(name, resDef.kind, resDef.url, useSelectedExtensions, policy, true, true, false); + }); + } else { + doTestBatchSD(sds, useSelectedExtensions, policy, true, true, false); + } + System.out.println("************************ END PROCESSING ******************************");