From a593e9a60da8b7e1384f4c5f9abc4be445c9327d Mon Sep 17 00:00:00 2001 From: dksharma Date: Mon, 27 Feb 2023 02:18:24 -0600 Subject: [PATCH] Minor correction to align with FHIR R5 RDF changes --- .../fhir/r5/conformance/ShExGenerator.java | 152 ++++++++++-------- .../hl7/fhir/r5/test/ShexGeneratorTests.java | 72 ++++++--- 2 files changed, 135 insertions(+), 89 deletions(-) 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 e20549756..3b799e59b 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 @@ -61,7 +61,9 @@ public class ShExGenerator { public boolean withComments = true; // include comments public boolean completeModel = false; // doing complete build (fhir.shex) - public boolean debugMode = false; + public boolean debugMode = false; // Used for Debugging and testing the code + + public boolean processConstraints = true; // set to false - to skip processing constraints public ConstraintTranslationPolicy constraintPolicy = ConstraintTranslationPolicy.ALL; @@ -643,25 +645,27 @@ public class ShExGenerator { isInnerType = true; } - // Process constraints - for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) { - String sdType = sd.getType(); - String cstype = constraint.getSource(); - if ((!cstype.isEmpty()) && (cstype.indexOf("/") != -1)) { - String[] els = cstype.split("/"); - cstype = els[els.length - 1]; - } + if (processConstraints) { + // Process constraints + for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) { + String sdType = sd.getType(); + String cstype = constraint.getSource(); + if ((!cstype.isEmpty()) && (cstype.indexOf("/") != -1)) { + String[] els = cstype.split("/"); + cstype = els[els.length - 1]; + } - String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath(); - String shortId = id.substring(id.lastIndexOf(".") + 1); - if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) { - if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) { - if (!isInnerType) { - debug("\n Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression()); - String transl = translateConstraint(sd, ed, constraint); - if (transl.isEmpty() || constraintsList.contains(transl)) - continue; - constraintsList.add(transl); + String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath(); + String shortId = id.substring(id.lastIndexOf(".") + 1); + if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) { + if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) { + if (!isInnerType) { + debug("\n Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression()); + String transl = translateConstraint(sd, ed, constraint); + if (transl.isEmpty() || constraintsList.contains(transl)) + continue; + constraintsList.add(transl); + } } } } @@ -679,24 +683,27 @@ public class ShExGenerator { } } - // Constraints for differential to cover constraints on SD itself without any elements of its own - for (ElementDefinition ded : sd.getDifferential().getElement()) { - // Process constraints - for (ElementDefinition.ElementDefinitionConstraintComponent dconstraint : ded.getConstraint()) { - String sdType = sd.getType(); + if (processConstraints) { + // Constraints for differential to cover constraints on SD itself without any elements of its own + for (ElementDefinition ded : sd.getDifferential().getElement()) { + // Process constraints + for (ElementDefinition.ElementDefinitionConstraintComponent dconstraint : ded.getConstraint()) { + String sdType = sd.getType(); - String id = ded.hasBase() ? ded.getBase().getPath() : ded.getPath(); - String shortId = id.substring(id.lastIndexOf(".") + 1); + String id = ded.hasBase() ? ded.getBase().getPath() : ded.getPath(); + String shortId = id.substring(id.lastIndexOf(".") + 1); - if (!isInInnerTypes(ded)) { - debug("\n Key: " + dconstraint.getKey() + " SD type: " + sd.getType() + " Element: " + ded.getPath() + " Constraint Source: " + dconstraint.getSource() + " Constraint:" + dconstraint.getExpression()); - String dtransl = translateConstraint(sd, ded, dconstraint); - if (dtransl.isEmpty() || constraintsList.contains(dtransl)) - continue; - constraintsList.add(dtransl); + if (!isInInnerTypes(ded)) { + debug("\n Key: " + dconstraint.getKey() + " SD type: " + sd.getType() + " Element: " + ded.getPath() + " Constraint Source: " + dconstraint.getSource() + " Constraint:" + dconstraint.getExpression()); + String dtransl = translateConstraint(sd, ded, dconstraint); + if (dtransl.isEmpty() || constraintsList.contains(dtransl)) + continue; + constraintsList.add(dtransl); + } } } } + shape_defn.add("elements", StringUtils.join(elements, "\n")); shape_defn.add("comment", root_comment == null? " " : "# " + root_comment); @@ -713,7 +720,12 @@ public class ShExGenerator { if (!sd.getContext().isEmpty()) { for (StructureDefinition.StructureDefinitionContextComponent uc : sd.getContext()) { if (!uc.getExpression().isEmpty()) { - String toStore = uc.getExpression() ; + String toStore = uc.getExpression(); + debug("CONTEXT-OF-USE FOUND: " + toStore); + if (toStore.indexOf("http") != -1) { + debug("\t\tWARNING: CONTEXT-OF-USE SKIPPED as it has 'http' in it, might be a URL, instead of '.' delimited string"); + continue; // some erroneous context of use may use a URL; ignore them + } String[] backRefs = toStore.split("\\."); toStore = "a [fhir:" + backRefs[0] + "]"; for (int i = 1; i < backRefs.length; i++) @@ -724,7 +736,13 @@ public class ShExGenerator { } } } - contextOfUseStr = "^fhir:extension { " + StringUtils.join(contextOfUse, " OR \n ") + "\n }"; + + if (!contextOfUse.isEmpty()) { + if (contextOfUse.size() > 1) + contextOfUseStr = "^fhir:extension { " + StringUtils.join(contextOfUse, "} OR \n {") + "}\n"; + else + contextOfUseStr = "^fhir:extension { " + contextOfUse.get(0) + "}\n"; + } } shape_defn.add("contextOfUse", contextOfUseStr); @@ -740,7 +758,8 @@ public class ShExGenerator { private String translateConstraint(StructureDefinition sd, ElementDefinition ed, ElementDefinition.ElementDefinitionConstraintComponent constraint){ String translated = ""; - if (false) { + if (constraint != null) { + //if (false) { String ce = constraint.getExpression(); String constItem = "FHIR-SD-Path:" + ed.getPath() + " Expression: " + ce; try { @@ -865,16 +884,16 @@ public class ShExGenerator { } break; case "And": - case "Implies" : - ops = " AND "; - break; + //case "Implies" : + ops = " AND "; + break; case "As": case "Is": ops = " a "; break; - case "Xor": - ops = " XOR "; - break; + //case "Xor": + //ops = " XOR "; // Although I implemented a routine for XOR, but that needs more testing. + // break; default: String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind(); if (!unMappedFunctions.contains(toStore)) @@ -1068,7 +1087,7 @@ public class ShExGenerator { else{ String mT = (mainTxt != null) ? mainTxt.trim() : ""; String dR = (mT.startsWith(".") || mT.startsWith("{") || mT.startsWith("[")) ? "" : "."; - return postProcessing(funCall.replaceFirst("CALLER", "CALLER" + dR + mainTxt ), nextText) ; + return postProcessing(funCall.replaceFirst("CALLER", Matcher.quoteReplacement("CALLER" + dR + mT )), nextText) ; } } @@ -1115,7 +1134,8 @@ public class ShExGenerator { qp = q.split("XOR")[1]; // because p xor q = ( p and not q) OR (not p and q) - return "(" + p + " AND NOT " + qp + ") OR ( NOT " + p + " AND " + qp + ")"; + //return "(" + p + " AND NOT " + qp + ") OR ( NOT " + p + " AND " + qp + ")"; + return "{" + p + "} AND NOT {" + qp + "} OR { NOT {" + p + "} AND " + qp + "} "; } return p + qp; @@ -1625,7 +1645,7 @@ public class ShExGenerator { String base = id.replace("[x]", ""); for (ElementDefinition.TypeRefComponent typ : ed.getType()) { - String entry = genChoiceEntry(sd, ed, "", "", typ); + String entry = genChoiceEntry(sd, ed, base, typ); refValues.clear(); if (typ.hasTargetProfile()) { typ.getTargetProfile().forEach((CanonicalType tps) -> { @@ -1644,14 +1664,13 @@ public class ShExGenerator { /** * Generate an entry in a choice list - * @param base base identifier + * @param id base identifier * @param typ type/discriminant * @return ShEx fragment for choice entry */ private String genChoiceEntry(StructureDefinition sd, ElementDefinition ed, String id, - String base, ElementDefinition.TypeRefComponent typ) { ST shex_choice_entry = tmplt(ELEMENT_TEMPLATE); @@ -1723,26 +1742,29 @@ public class ShExGenerator { element_reference.add("elements", StringUtils.join(elements, "\n")); List innerConstraintsList = new ArrayList(); - // Process constraints - for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) { - String sdType = sd.getType(); - String cstype = constraint.getSource(); - if ((cstype != null) && (cstype.indexOf("/") != -1)) { - String[] els = cstype.split("/"); - cstype = els[els.length - 1]; - } - String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath(); - String shortId = id.substring(id.lastIndexOf(".") + 1); - if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) { - if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) { - //if (!isInInnerTypes(ed)) { - debug("\n (INNER ED) Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression()); - String transl = translateConstraint(sd, ed, constraint); - if (transl.isEmpty() || innerConstraintsList.contains(transl)) - continue; - innerConstraintsList.add(transl); - //} + if (processConstraints) { + // Process constraints + for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) { + String sdType = sd.getType(); + String cstype = constraint.getSource(); + if ((cstype != null) && (cstype.indexOf("/") != -1)) { + String[] els = cstype.split("/"); + cstype = els[els.length - 1]; + } + + String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath(); + String shortId = id.substring(id.lastIndexOf(".") + 1); + if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) { + if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) { + //if (!isInInnerTypes(ed)) { + debug("\n (INNER ED) Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression()); + String transl = translateConstraint(sd, ed, constraint); + if (transl.isEmpty() || innerConstraintsList.contains(transl)) + continue; + innerConstraintsList.add(transl); + //} + } } } } 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 5786a6789..328119052 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 @@ -34,18 +34,22 @@ public class ShexGeneratorTests { } 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()); + // ------- Comment following for debugging/testing + 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.doTestSingleSD(name.toLowerCase(), cat, name, - false, ShExGenerator.ConstraintTranslationPolicy.ALL, - true, true, false); + // Test with processing constraints flag + // ----------------- Uncomment following to testing/Debugging ----- +// boolean processConstraints = true; +// this.doTestSingleSD(name.toLowerCase(), cat, name, +// false, ShExGenerator.ConstraintTranslationPolicy.ALL, +// true, true, false, processConstraints); } @Test @@ -128,7 +132,7 @@ public class ShexGeneratorTests { doTest("Element", ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITION); } - @Test + @Ignore public void doTestAllSingleSDMode() throws FileNotFoundException, IOException, FHIRException, UcumException { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); @@ -139,11 +143,13 @@ public class ShexGeneratorTests { // Process all types of constraints, do not skip ShExGenerator.ConstraintTranslationPolicy.ALL, // BatchMode - All Shex Schemas in one single file - false + false, + // process constraints or not + true ); } - @Test + @Ignore public void doTestAllBatchMode() throws FileNotFoundException, IOException, FHIRException, UcumException { List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class); @@ -154,6 +160,8 @@ public class ShexGeneratorTests { // Process all types of constraints, do not skip ShExGenerator.ConstraintTranslationPolicy.ALL, // BatchMode - All Shex Schemas in one single file + true, + // process constraints or not true ); } @@ -168,7 +176,9 @@ public class ShexGeneratorTests { false, //Process all extensions ShExGenerator.ConstraintTranslationPolicy.GENERIC_ONLY, // Process generic constraints only, ignore constraints of type 'context of use' - false + false, + // process constraints or not + true ); } @@ -183,7 +193,9 @@ public class ShexGeneratorTests { false, //Process all extensions ShExGenerator.ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY, // Process constraints only where context of use found, skip otherwise - false + false, + // process constraints or not + true ); } @@ -196,7 +208,9 @@ public class ShexGeneratorTests { sds, // List of Structure Definitions true, //Process only given/selected extensions, ignore other extensions ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints - false + false, + // process constraints or not + true ); } @@ -209,7 +223,9 @@ public class ShexGeneratorTests { sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints - false + false, + // process constraints or not + true ); } @@ -222,7 +238,9 @@ public class ShexGeneratorTests { sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints - false + false, + // process constraints or not + true ); } @@ -235,7 +253,9 @@ public class ShexGeneratorTests { sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints - false + false, + // process constraints or not + true ); } @@ -247,7 +267,9 @@ public class ShexGeneratorTests { sds, // List of Structure Definitions false, //Process only given/selected extensions, ignore other extensions ShExGenerator.ConstraintTranslationPolicy.ALL, // Process all type of constraints - false + false, + // process constraints or not + true ); } @@ -255,7 +277,7 @@ public class ShexGeneratorTests { List sds, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, - boolean batchMode) { + boolean batchMode, boolean processConstraints) { if ((sds == null) || (sds.isEmpty())) { throw new FHIRException("No StructuredDefinition found!"); } @@ -276,10 +298,10 @@ public class ShexGeneratorTests { name = els[els.length - 1]; } System.out.println("******************** " + resDef + " *********************"); - doTestSingleSD(name, resDef.kind, resDef.url, useSelectedExtensions, policy, true, true, false); + doTestSingleSD(name, resDef.kind, resDef.url, useSelectedExtensions, policy, true, true, false, processConstraints); }); } else { - doTestBatchSD(sds, useSelectedExtensions, policy, true, true, false); + doTestBatchSD(sds, useSelectedExtensions, policy, true, true, false, processConstraints); } System.out.println("************************ END PROCESSING ******************************"); @@ -294,7 +316,7 @@ public class ShexGeneratorTests { String name, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx, - boolean excludeMetaSDs) { + boolean excludeMetaSDs, boolean processConstraints) { IWorkerContext ctx = TestingUtilities.getSharedWorkerContext(); StructureDefinition sd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); if (sd == null) { @@ -306,6 +328,7 @@ public class ShexGeneratorTests { this.shexGenerator = new ShExGenerator(ctx); this.shexGenerator.debugMode = debugMode; + this.shexGenerator.processConstraints = processConstraints; this.shexGenerator.constraintPolicy = policy; if (excludeMetaSDs) { @@ -350,7 +373,7 @@ public class ShexGeneratorTests { private void doTestBatchSD(List sds, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, - boolean validateShEx, boolean excludeMetaSDs) { + boolean validateShEx, boolean excludeMetaSDs, boolean processConstraints) { 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"); @@ -358,6 +381,7 @@ public class ShexGeneratorTests { this.shexGenerator = new ShExGenerator(ctx); this.shexGenerator.debugMode = debugMode; + this.shexGenerator.processConstraints = processConstraints; this.shexGenerator.constraintPolicy = policy; if (excludeMetaSDs) {