From cc983073cdc1dbdf032edfd43dc67f5830ecd6a7 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 8 Mar 2022 13:19:19 +1100 Subject: [PATCH 01/11] more cross version extensions --- RELEASE_NOTES.md | 9 ++++++++- .../java/org/hl7/fhir/r5/utils/XVerExtensionManager.java | 6 ------ .../java/org/hl7/fhir/utilities/npm/CommonPackages.java | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b06c6ab5..c689c8032 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,4 +4,11 @@ ## Other code changes -* no changes \ No newline at end of file +* no changes + +more presenting extensions when rendering. +4f652bc +fix handling of xver extensions with choice sub-extensions + + Get package id from 'package' field when 'npm-package' unavailable + \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java index 7d27c4782..2b435782d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java @@ -50,9 +50,6 @@ public class XVerExtensionManager { return XVerExtensionStatus.Invalid; } String v = url.substring(20, 23); - if ("5.0".equals(v)) { - v = "4.6"; // for now - } String e = url.substring(54); if (!lists.containsKey(v)) { if (context.getBinaries().containsKey("xver-paths-"+v+".json")) { @@ -86,9 +83,6 @@ public class XVerExtensionManager { public StructureDefinition makeDefinition(String url) { String verSource = url.substring(20, 23); - if ("5.0".equals(verSource)) { - verSource = "4.6"; // for now - } String verTarget = VersionUtilities.getMajMin(context.getVersion()); String e = url.substring(54); JsonObject root = lists.get(verSource); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java index 284cabed8..6394384b3 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java @@ -3,7 +3,7 @@ package org.hl7.fhir.utilities.npm; public class CommonPackages { public static final String ID_XVER = "hl7.fhir.xver-extensions"; - public static final String VER_XVER = "0.0.8"; + public static final String VER_XVER = "0.0.9"; public static final String ID_PUBPACK = "hl7.fhir.pubpack"; public static final String VER_PUBPACK = "0.0.9"; From d7754a321f30bb647c603c231b2fcdaf24828e44 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 9 Mar 2022 17:36:39 +1100 Subject: [PATCH 02/11] Fix snapshot generation - always populate constraint source properly --- .../fhir/r5/conformance/ProfileUtilities.java | 15 +++++++++ .../fhir/r5/test/ProfileUtilitiesTests.java | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 26aeb35e0..55caa008f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -1091,6 +1091,7 @@ public class ProfileUtilities extends TranslatingUtilities { ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); updateFromBase(outcome, currentBase); + updateConstraintSources(outcome, srcSD.getUrl()); markDerived(outcome); if (resultPathBase == null) resultPathBase = outcome.getPath(); @@ -1927,6 +1928,15 @@ public class ProfileUtilities extends TranslatingUtilities { return res; } + private void updateConstraintSources(ElementDefinition ed, String url) { + for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { + if (!c.hasSource()) { + c.setSource(url); + } + } + + } + private Set getListOfTypes(ElementDefinition e) { Set result = new HashSet<>(); for (TypeRefComponent t : e.getType()) { @@ -2422,6 +2432,11 @@ public class ProfileUtilities extends TranslatingUtilities { StructureDefinition sd = null; if (type.hasProfile()) { sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue()); + if (sd == null) { + if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) { + sd = xver.makeDefinition(type.getProfile().get(0).getValue()); + } + } if (sd == null) System.out.println("Failed to find referenced profile: " + type.getProfile()); } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java index e5a62e947..c7e650f8e 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java @@ -7,6 +7,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; @@ -51,6 +52,12 @@ public class ProfileUtilitiesTests { f.setBase(null); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } ok = Base.compareDeep(b, f, true); } } @@ -83,6 +90,12 @@ public class ProfileUtilitiesTests { if (ok) { ElementDefinition b = base.getSnapshot().getElement().get(i); ElementDefinition f = focus.getSnapshot().getElement().get(i); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) ok = false; else { @@ -128,6 +141,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } @@ -174,6 +193,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } @@ -221,6 +246,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } From 89b912dde29e63fed0c84b22360d7b9c66041650 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 9 Mar 2022 17:37:26 +1100 Subject: [PATCH 03/11] Fix cross version extension validation, and check that extensions are consistent wrt isModifier in profiles --- .../fhir/r5/utils/XVerExtensionManager.java | 4 +++- .../fhir/utilities/i18n/I18nConstants.java | 2 ++ .../fhir/utilities/npm/CommonPackages.java | 2 +- .../src/main/resources/Messages.properties | 2 ++ .../type/StructureDefinitionValidator.java | 23 +++++++++++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java index 2b435782d..d78178566 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java @@ -117,16 +117,18 @@ public class XVerExtensionManager { populateTypes(path, val, verSource, verTarget); } else if (path.has("elements")) { for (JsonElement i : path.getAsJsonArray("elements")) { + JsonObject elt = root.getAsJsonObject(e+"."+i.getAsString()); + if (elt != null) { String s = i.getAsString().replace("[x]", ""); sd.getDifferential().addElement().setPath("Extension.extension").setSliceName(s); sd.getDifferential().addElement().setPath("Extension.extension.extension").setMax("0"); sd.getDifferential().addElement().setPath("Extension.extension.url").setFixed(new UriType(s)); ElementDefinition val = sd.getDifferential().addElement().setPath("Extension.extension.value[x]").setMin(1); - JsonObject elt = root.getAsJsonObject(e+"."+i.getAsString()); if (!elt.has("types")) { throw new FHIRException("Internal error - nested elements not supported yet"); } populateTypes(elt, val, verSource, verTarget); + } } sd.getDifferential().addElement().setPath("Extension.url").setFixed(new UriType(url)); sd.getDifferential().addElement().setPath("Extension.value[x]").setMax("0"); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index b8595a111..a3d1cf554 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -366,6 +366,8 @@ public class I18nConstants { public static final String SD_ED_TYPE_PROFILE_UNKNOWN = "SD_ED_TYPE_PROFILE_UNKNOWN"; public static final String SD_ED_TYPE_PROFILE_NOTYPE = "SD_ED_TYPE_PROFILE_NOTYPE"; public static final String SD_ED_TYPE_PROFILE_WRONG = "SD_ED_TYPE_PROFILE_WRONG"; + public static final String SD_ED_TYPE_PROFILE_IS_MODIFIER = "SD_ED_TYPE_PROFILE_IS_MODIFIER"; + public static final String SD_ED_TYPE_PROFILE_NOT_MODIFIER = "SD_ED_TYPE_PROFILE_NOT_MODIFIER"; public static final String SD_ED_TYPE_PROFILE_WRONG_TARGET = "SD_ED_TYPE_PROFILE_WRONG_TARGET"; public static final String SD_ED_TYPE_NO_TARGET_PROFILE = "SD_ED_TYPE_NO_TARGET_PROFILE"; public static final String SD_ED_SHOULD_BIND = "SD_ED_SHOULD_BIND"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java index 6394384b3..f62731099 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java @@ -3,7 +3,7 @@ package org.hl7.fhir.utilities.npm; public class CommonPackages { public static final String ID_XVER = "hl7.fhir.xver-extensions"; - public static final String VER_XVER = "0.0.9"; + public static final String VER_XVER = "0.0.11"; public static final String ID_PUBPACK = "hl7.fhir.pubpack"; public static final String VER_PUBPACK = "0.0.9"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 921678728..fa1748615 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -629,6 +629,8 @@ Unable_to_connect_to_terminology_server = Unable to connect to terminology serve SD_ED_TYPE_PROFILE_UNKNOWN = Unable to resolve profile {0} SD_ED_TYPE_PROFILE_NOTYPE = Found profile {0}, but unable to determine the type it applies to SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but the {3} element has type {2} +SD_ED_TYPE_PROFILE_IS_MODIFIER = Profile {0} is for not for a modifier extension, but the {3} element is a modifier +SD_ED_TYPE_PROFILE_NOT_MODIFIER = Profile {0} is for a modifier extension, but the {3} element is not a modifier SD_ED_TYPE_PROFILE_WRONG_TARGET = Profile {0} is for type {1}, which is not a {4} (which is required because the {3} element has type {2}) SD_ED_TYPE_NO_TARGET_PROFILE = Type {0} does not allow for target Profiles TERMINOLOGY_TX_NOSVC_BOUND_REQ = Could not confirm that the codes provided are from the required value set {0} because there is no terminology service diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 7eb98cc1b..0bbd1792a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -294,6 +294,15 @@ public class StructureDefinitionValidator extends BaseValidator { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + if (t.getType().equals("Extension")) { + boolean isModifierDefinition = checkIsModifierExtension(sd); + boolean isModifierContext = path.endsWith(".modifierExtension"); + if (isModifierDefinition) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isModifierContext, I18nConstants.SD_ED_TYPE_PROFILE_NOT_MODIFIER, p, t, code, path); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !isModifierContext, I18nConstants.SD_ED_TYPE_PROFILE_IS_MODIFIER, p, t, code, path); + } + } } } } @@ -325,10 +334,24 @@ public class StructureDefinitionValidator extends BaseValidator { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else if (!isInstanceOf(t, code)) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + } else { + if (t.getType().equals("Extension")) { + boolean isModifierDefinition = checkIsModifierExtension(sd); + boolean isModifierContext = path.endsWith(".modifierExtension"); + if (isModifierDefinition) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isModifierContext, I18nConstants.SD_ED_TYPE_PROFILE_NOT_MODIFIER, p, t, code, path); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !isModifierContext, I18nConstants.SD_ED_TYPE_PROFILE_IS_MODIFIER, p, t, code, path); + } + } } } } + private boolean checkIsModifierExtension(StructureDefinition t) { + return t.getSnapshot().getElementFirstRep().getIsModifier(); + } + private void validateTargetProfile(List errors, Element profile, String code, NodeStack stack, String path) { String p = profile.primitiveValue(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); From f9c3ac3c0adc9245397c4c9c1026afa156bd49a3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 9 Mar 2022 21:31:57 +1100 Subject: [PATCH 04/11] test case version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cfce9bbe8..dbb6f8c5a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.4.0 - 1.1.90 + 1.1.92 5.7.1 1.7.1 3.0.0-M5 From 603bd0407d0bd8cbaa550dc1f17c596b05de1fbc Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 10 Mar 2022 06:52:26 +1100 Subject: [PATCH 05/11] fix r4b tests From 33f6c57d487d9103819cc63093d429689f82dfe0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 10 Mar 2022 06:52:44 +1100 Subject: [PATCH 06/11] fix NPE --- .../java/org/hl7/fhir/r5/conformance/ProfileUtilities.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 55caa008f..8e9e0bf51 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -4329,12 +4329,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean isBaseCondition(IdType c) { String key = c.asStringValue(); - return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); } private boolean isBaseConstraint(ElementDefinitionConstraintComponent con) { String key = con.getKey(); - return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); } private void makeChoiceRows(List subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode) { From a34c33c0c01f0ea209e1641769ea8330107ad53f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 10 Mar 2022 07:43:01 +1100 Subject: [PATCH 07/11] =fix r4b tests --- .../convertors/misc/XVerPackegeFixer.java | 190 +++++++++++++----- .../fhir/r4b/comparison/ProfileComparer.java | 156 +++++++++----- .../r4b/conformance/ProfileUtilities.java | 123 +++++++++--- .../hl7/fhir/r4b/utils/ToolingExtensions.java | 5 +- .../fhir/r4b/utils/XVerExtensionManager.java | 18 +- .../fhir/r4b/test/ProfileUtilitiesTests.java | 31 +++ .../r4b/test/SnapShotGenerationTests.java | 60 +++--- 7 files changed, 420 insertions(+), 163 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java index 634e3107b..8335423de 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java @@ -6,13 +6,19 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import javax.naming.ldap.StartTlsRequest; import javax.xml.parsers.ParserConfigurationException; +import org.hl7.fhir.convertors.misc.XVerPackegeFixer.References; +import org.hl7.fhir.dstu2.model.ElementDefinition; +import org.hl7.fhir.dstu2.model.StructureDefinition; +import org.hl7.fhir.dstu2.model.StructureDefinition.StructureDefinitionSnapshotComponent; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.json.JsonTrackingParser; @@ -24,12 +30,35 @@ import com.google.gson.JsonObject; public class XVerPackegeFixer { - private static final String R5_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r5.core\\package"; - private static final String R4_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.core\\package"; - private static final String R3_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r3.core\\package"; - private static final String R2B_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2b.core\\package"; - private static final String R2_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2.core\\package"; - private static int mod; + public static class References { + private boolean modifier; + private boolean inherited; + + public boolean getModifier() { + return modifier; + } + + public void setModifier(boolean value) { + this.modifier = value; + } + + public boolean getInherited() { + return inherited; + } + + public void setInherited(boolean inherited) { + this.inherited = inherited; + } + + } + + + private static final String R5_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r5.core/package"; + private static final String R4_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r4.core/package"; + private static final String R3_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r3.core/package"; + private static final String R2B_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r2b.core/package"; + private static final String R2_FOLDER = "/Users/grahamegrieve/work/packages/hl7.fhir.rX/hl7.fhir.r2.core/package"; + private static int modCount; private static Map map5 = new HashMap<>(); private static Map map4 = new HashMap<>(); @@ -38,21 +67,23 @@ public class XVerPackegeFixer { private static Map map2b = new HashMap<>(); public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException { - mod = 0; + modCount = 0; for (File f : new File(args[0]).listFiles()) { - if (f.getName().startsWith("xver-")) { + if (f.getName().startsWith("xver-") && f.getName().contains("1.0")) { JsonObject j = JsonTrackingParser.parseJson(f); fixUp(j, f.getName()); JsonTrackingParser.write(j, f, true); } } - System.out.println("all done: "+mod+" modifiers"); + System.out.println("all done: "+modCount+" modifiers"); } private static void fixUp(JsonObject j, String name) throws FHIRFormatError, FileNotFoundException, IOException { name = name.replace(".json", ""); System.out.println("Process "+name); String version = name.substring(name.lastIndexOf("-")+1); + Set pr = new HashSet<>(); + int i = 0; for (Entry e : j.entrySet()) { if (i == 50) { @@ -62,18 +93,31 @@ public class XVerPackegeFixer { i++; String n = e.getKey(); JsonObject o = ((JsonObject) e.getValue()); - boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0); - if (!ok) { + // boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0); +// if (!ok) { + if (o.has("types")) { + o.remove("types"); + } + if (o.has("elements")) { + o.remove("elements"); + } + if (o.has("modifier")) { + o.remove("modifier"); + } List types = new ArrayList<>(); List elements = new ArrayList<>(); - getElementInfo(version, n, types, elements); + References mod = new References(); + getElementInfo(version, n, types, elements, mod); + if (mod.getInherited()) { + pr.add(n); + } if (elements.size() > 0) { JsonArray arr = o.getAsJsonArray("elements"); if (arr == null) { arr = new JsonArray(); o.add("elements", arr); } - for (String s : types) { + for (String s : elements) { arr.add(s); } } else if (types.size() > 0) { @@ -86,30 +130,37 @@ public class XVerPackegeFixer { arr.add(s); } } - } + if (mod.getModifier()) { + o.addProperty("modifier", true); + modCount++; + } +// } } + for (String s : pr) { + j.remove(s); + } System.out.println("done"); } - private static boolean getElementInfo(String version, String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { - if ("contained".equals(n.substring(n.indexOf(".")+1))) { - return false; - } + private static boolean getElementInfo(String version, String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { +// if ("contained".equals(n.substring(n.indexOf(".")+1))) { +// return false; +// } switch (version) { - case "4.6": return getElementInfoR5(n, types, elements); - case "4.0": return getElementInfoR4(n, types, elements); - case "3.0": return getElementInfoR3(n, types, elements); - case "1.4": return getElementInfoR2B(n, types, elements); - case "1.0": return getElementInfoR2(n, types, elements); + case "4.6": return getElementInfoR5(n, types, elements, mod); + case "4.0": return getElementInfoR4(n, types, elements, mod); + case "3.0": return getElementInfoR3(n, types, elements, mod); + case "1.4": return getElementInfoR2B(n, types, elements, mod); + case "1.0": return getElementInfoR2(n, types, elements, mod); } return false; } - private static Object tail(String value) { + private static String tail(String value) { return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value; } - private static boolean getElementInfoR5(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + private static boolean getElementInfoR5(String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { String tn = n.substring(0, n.indexOf(".")); org.hl7.fhir.r5.model.StructureDefinition sd = null; if (map5.containsKey(tn)) { @@ -120,7 +171,8 @@ public class XVerPackegeFixer { } for (org.hl7.fhir.r5.model.ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(n)) { - List children = listChildrenR5(sd.getSnapshot().getElement(), ed); + mod.setModifier(ed.getIsModifier()); + List children = listChildrenR5(sd.getSnapshot().getElement(), ed); if (children.size() > 0) { for (org.hl7.fhir.r5.model.ElementDefinition c : children) { String en = c.getPath().substring(ed.getPath().length()+1); @@ -164,7 +216,7 @@ public class XVerPackegeFixer { return res; } - private static boolean getElementInfoR4(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + private static boolean getElementInfoR4(String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { String tn = n.substring(0, n.indexOf(".")); org.hl7.fhir.r4.model.StructureDefinition sd = null; if (map4.containsKey(tn)) { @@ -175,6 +227,7 @@ public class XVerPackegeFixer { } for (org.hl7.fhir.r4.model.ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(n)) { + mod.setModifier(ed.getIsModifier()); List children = listChildrenR4(sd.getSnapshot().getElement(), ed); if (children.size() > 0) { for (org.hl7.fhir.r4.model.ElementDefinition c : children) { @@ -220,7 +273,7 @@ public class XVerPackegeFixer { } - private static boolean getElementInfoR3(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + private static boolean getElementInfoR3(String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { String tn = n.substring(0, n.indexOf(".")); org.hl7.fhir.dstu3.model.StructureDefinition sd = null; if (map3.containsKey(tn)) { @@ -231,6 +284,7 @@ public class XVerPackegeFixer { } for (org.hl7.fhir.dstu3.model.ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(n)) { + mod.setModifier(ed.getIsModifier()); List children = listChildrenR3(sd.getSnapshot().getElement(), ed); if (children.size() > 0) { for (org.hl7.fhir.dstu3.model.ElementDefinition c : children) { @@ -272,17 +326,14 @@ public class XVerPackegeFixer { } - private static boolean getElementInfoR2(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + private static boolean getElementInfoR2(String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { String tn = n.substring(0, n.indexOf(".")); org.hl7.fhir.dstu2.model.StructureDefinition sd = null; - if (map2.containsKey(tn)) { - sd = map2.get(tn); - } else { - sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json"))); - map2.put(tn, sd); - } + sd = loadType(tn); + mod.setInherited(isInherited(n, sd)); for (org.hl7.fhir.dstu2.model.ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(n)) { + mod.setModifier(ed.getIsModifier()); List children = listChildrenR2(sd.getSnapshot().getElement(), ed); if (children.size() > 0) { for (org.hl7.fhir.dstu2.model.ElementDefinition c : children) { @@ -290,28 +341,76 @@ public class XVerPackegeFixer { elements.add(en); } } else { + boolean isReference = false; + StringBuilder r = new StringBuilder(); + r.append("Reference("); for (org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent t : ed.getType()) { if (t.hasProfile()) { - StringBuilder b = new StringBuilder(); - b.append(t.getCode()); - b.append("("); - boolean first = true; - for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) { - if (first) first = false; else b.append("|"); - b.append(tail(u.getValue())); + if ("Reference".equals(t.getCode())) { + for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) { + if (isReference) + r.append("|"); + r.append(tail(u.getValue())); + isReference = true; + } + } else { + StringBuilder b = new StringBuilder(); + b.append(t.getCode()); + b.append("("); + boolean first = true; + for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) { + if (first) first = false; else b.append("|"); + b.append(tail(u.getValue())); + } + b.append(")"); + types.add(b.toString()); } - b.append(")"); - types.add(b.toString()); } else { types.add(t.getCode()); } } + if (isReference) { + r.append(")"); + types.add(r.toString()); + } } } } return false; } + private static org.hl7.fhir.dstu2.model.StructureDefinition loadType(String tn) + throws IOException, FileNotFoundException { + org.hl7.fhir.dstu2.model.StructureDefinition sd; + if (map2.containsKey(tn)) { + sd = map2.get(tn); + } else { + sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json"))); + map2.put(tn, sd); + } + return sd; + } + + + private static boolean isInherited(String n, StructureDefinition sd) throws FileNotFoundException, IOException { + String tail = n.substring(n.indexOf(".")+1); + while (sd != null) { + sd = sd.hasBase() ? loadType(tail(sd.getBase())) : null; + if (sd != null && hasPath(sd.getSnapshot(), sd.getName()+"."+tail)) { + return true; + } + } + return false; + } + + private static boolean hasPath(StructureDefinitionSnapshotComponent snapshot, String path) { + for (ElementDefinition ed : snapshot.getElement()) { + if (ed.getPath().equals(path)) { + return true; + } + } + return false; + } private static List listChildrenR2(List list, org.hl7.fhir.dstu2.model.ElementDefinition ed) { List res = new ArrayList<>(); @@ -328,7 +427,7 @@ public class XVerPackegeFixer { } - private static boolean getElementInfoR2B(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + private static boolean getElementInfoR2B(String n, List types, List elements, References mod) throws FHIRFormatError, FileNotFoundException, IOException { String tn = n.substring(0, n.indexOf(".")); org.hl7.fhir.dstu2016may.model.StructureDefinition sd = null; if (map2b.containsKey(tn)) { @@ -339,6 +438,7 @@ public class XVerPackegeFixer { } for (org.hl7.fhir.dstu2016may.model.ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(n)) { + mod.setModifier(ed.getIsModifier()); List children = listChildrenR2B(sd.getSnapshot().getElement(), ed); if (children.size() > 0) { for (org.hl7.fhir.dstu2016may.model.ElementDefinition c : children) { diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ProfileComparer.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ProfileComparer.java index 7d7d9bd33..c6ebdf81a 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ProfileComparer.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/comparison/ProfileComparer.java @@ -45,14 +45,14 @@ public class ProfileComparer extends CanonicalResourceComparer { public class ProfileComparison extends CanonicalResourceComparison { - private StructuralMatch combined; + private StructuralMatch combined; public ProfileComparison(StructureDefinition left, StructureDefinition right) { super(left, right); - combined = new StructuralMatch(); // base + combined = new StructuralMatch(); // base } - public StructuralMatch getCombined() { + public StructuralMatch getCombined() { return combined; } @@ -79,7 +79,21 @@ public class ProfileComparer extends CanonicalResourceComparer { } - + private class ElementDefinitionNode { + private ElementDefinition def; + private StructureDefinition src; + private ElementDefinitionNode(StructureDefinition src, ElementDefinition def) { + super(); + this.src = src; + this.def = def; + } + public ElementDefinition getDef() { + return def; + } + public StructureDefinition getSrc() { + return src; + } + } private ProfileUtilities utilsLeft; private ProfileUtilities utilsRight; @@ -127,7 +141,7 @@ public class ProfileComparer extends CanonicalResourceComparer { if (left.getType().equals(right.getType())) { DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left); DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right); - StructuralMatch sm = new StructuralMatch(ln.current(), rn.current()); + StructuralMatch sm = new StructuralMatch(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current())); compareElements(res, sm, ln.path(), null, ln, rn); res.combined = sm; } @@ -147,7 +161,7 @@ public class ProfileComparer extends CanonicalResourceComparer { throw new DefinitionException("StructureDefinition snapshot is empty ("+name+": "+sd.getName()+")"); } - private void compareElements(ProfileComparison comp, StructuralMatch res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException { + private void compareElements(ProfileComparison comp, StructuralMatch res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException { assert(path != null); assert(left != null); assert(right != null); @@ -190,7 +204,7 @@ public class ProfileComparer extends CanonicalResourceComparer { subset.setExample(left.current().hasExample() ? left.current().getExample() : right.current().getExample()); if (left.current().getMustSupport() != right.current().getMustSupport()) { - vm(IssueSeverity.ERROR, "Elements differ in definition for mustSupport:\r\n \""+left.current().getMustSupport()+"\"\r\n \""+right.current().getMustSupport()+"\"", path, comp.getMessages(), res.getMessages()); + vm(IssueSeverity.WARNING, "Elements differ in definition for mustSupport: '"+left.current().getMustSupport()+"' vs '"+right.current().getMustSupport()+"'", path, comp.getMessages(), res.getMessages()); } subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport()); @@ -198,15 +212,20 @@ public class ProfileComparer extends CanonicalResourceComparer { // compare and intersect - superset.setMin(unionMin(left.current().getMin(), right.current().getMin())); - superset.setMax(unionMax(left.current().getMax(), right.current().getMax())); - subset.setMin(intersectMin(left.current().getMin(), right.current().getMin())); - subset.setMax(intersectMax(left.current().getMax(), right.current().getMax())); - rule(comp, res, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); + int leftMin = left.current().getMin(); + int rightMin = right.current().getMin(); + int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(left.current().getMax()); + int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(right.current().getMax()); + + checkMinMax(comp, res, path, leftMin, rightMin, leftMax, rightMax); + superset.setMin(unionMin(leftMin, rightMin)); + superset.setMax(unionMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); + subset.setMin(intersectMin(leftMin, rightMin)); + subset.setMax(intersectMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); superset.getType().addAll(unionTypes(comp, res, path, left.current().getType(), right.current().getType())); subset.getType().addAll(intersectTypes(comp, res, subset, path, left.current().getType(), right.current().getType())); - rule(comp, res, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch:\r\n "+typeCode(left)+"\r\n "+typeCode(right)); + rule(comp, res, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch: "+typeCode(left)+" vs "+typeCode(right)); // // superset.setMaxLengthElement(unionMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); @@ -283,7 +302,8 @@ public class ProfileComparer extends CanonicalResourceComparer { // return null; } - private void compareChildren(ProfileComparison comp, StructuralMatch res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { + + private void compareChildren(ProfileComparison comp, StructuralMatch res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { List lc = left.children(); List rc = right.children(); // it's possible that one of these profiles walks into a data type and the other doesn't @@ -299,10 +319,10 @@ public class ProfileComparer extends CanonicalResourceComparer { DefinitionNavigator r = findInList(rc, l); if (r == null) { comp.getUnion().getSnapshot().getElement().add(l.current().copy()); - res.getChildren().add(new StructuralMatch(l.current(), vmI(IssueSeverity.INFORMATION, "Removed this element", path))); + res.getChildren().add(new StructuralMatch(new ElementDefinitionNode(l.getStructure(), l.current()), vmI(IssueSeverity.INFORMATION, "Removed this element", path))); } else { matchR.add(r); - StructuralMatch sm = new StructuralMatch(l.current(), r.current()); + StructuralMatch sm = new StructuralMatch(new ElementDefinitionNode(l.getStructure(), l.current()), new ElementDefinitionNode(r.getStructure(), r.current())); res.getChildren().add(sm); compareElements(comp, sm, l.path(), null, l, r); } @@ -310,7 +330,7 @@ public class ProfileComparer extends CanonicalResourceComparer { for (DefinitionNavigator r : rc) { if (!matchR.contains(r)) { comp.getUnion().getSnapshot().getElement().add(r.current().copy()); - res.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this element", path), r.current())); + res.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this element", path), new ElementDefinitionNode(r.getStructure(), r.current()))); } } } @@ -324,7 +344,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return null; } - private void ruleEqual(ProfileComparison comp, StructuralMatch res, DataType vLeft, DataType vRight, String name, String path) throws IOException { + private void ruleEqual(ProfileComparison comp, StructuralMatch res, DataType vLeft, DataType vRight, String name, String path) throws IOException { if (vLeft == null && vRight == null) { // nothing } else if (vLeft == null) { @@ -338,7 +358,7 @@ public class ProfileComparer extends CanonicalResourceComparer { private String toString(DataType val, boolean left) throws IOException { if (val instanceof PrimitiveType) - return "\"" + ((PrimitiveType) val).getValueAsString()+"\""; + return "'" + ((PrimitiveType) val).getValueAsString()+"'"; IParser jp = left ? session.getContextLeft().newJsonParser() : session.getContextRight().newJsonParser(); return jp.composeString(val, "value"); @@ -356,14 +376,14 @@ public class ProfileComparer extends CanonicalResourceComparer { return s; } - private boolean rule(ProfileComparison comp, StructuralMatch res, boolean test, String path, String message) { + private boolean rule(ProfileComparison comp, StructuralMatch res, boolean test, String path, String message) { if (!test) { vm(IssueSeverity.ERROR, message, path, comp.getMessages(), res.getMessages()); } return test; } - private String mergeText(ProfileComparison comp, StructuralMatch res, String path, String name, String left, String right, boolean isError) { + private String mergeText(ProfileComparison comp, StructuralMatch res, String path, String name, String left, String right, boolean isError) { if (left == null && right == null) return null; if (left == null) @@ -375,7 +395,7 @@ public class ProfileComparer extends CanonicalResourceComparer { if (left.equalsIgnoreCase(right)) return left; if (path != null) { - vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+":\r\n \""+left+"\"\r\n \""+right+"\"", path, comp.getMessages(), res.getMessages()); + vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+": '"+left+"' vs '"+right+"'", path, comp.getMessages(), res.getMessages()); } return "left: "+left+"; right: "+right; } @@ -429,6 +449,36 @@ public class ProfileComparer extends CanonicalResourceComparer { return right; } + private void checkMinMax(ProfileComparison comp, StructuralMatch res, String path, int leftMin, int rightMin, int leftMax, int rightMax) { + if (leftMin != rightMin) { + if (leftMin == 0) { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } else if (rightMin == 0) { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } else { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } + } + if (leftMax != rightMax) { + if (leftMax == Integer.MAX_VALUE) { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } else if (rightMax == Integer.MAX_VALUE) { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } else { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } + } +// rule(comp, res, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); + + // cross comparison - if max > min in either direction, there can be no instances that are valid against both + if (leftMax < rightMin) { + vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); + } + if (rightMax < leftMin) { + vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); + } + } + private int unionMin(int left, int right) { if (left > right) return right; @@ -436,18 +486,14 @@ public class ProfileComparer extends CanonicalResourceComparer { return left; } - private String intersectMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); + private String intersectMax(int l, int r, String left, String right) { if (l < r) return left; else return right; } - private String unionMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); + private String unionMax(int l, int r, String left, String right) { if (l < r) return right; else @@ -480,7 +526,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return Integer.toString(defn.current().getMin())+".."+defn.current().getMax(); } - private Collection unionTypes(ProfileComparison comp, StructuralMatch res, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { + private Collection unionTypes(ProfileComparison comp, StructuralMatch res, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { List result = new ArrayList(); for (TypeRefComponent l : left) checkAddTypeUnion(comp, res, path, result, l, session.getContextLeft()); @@ -489,7 +535,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return result; } - private void checkAddTypeUnion(ProfileComparison comp, StructuralMatch res, String path, List results, TypeRefComponent nw, IWorkerContext ctxt) throws DefinitionException, IOException, FHIRFormatError { + private void checkAddTypeUnion(ProfileComparison comp, StructuralMatch res, String path, List results, TypeRefComponent nw, IWorkerContext ctxt) throws DefinitionException, IOException, FHIRFormatError { boolean pfound = false; boolean tfound = false; nw = nw.copy(); @@ -586,7 +632,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return false; } - private Collection intersectTypes(ProfileComparison comp, StructuralMatch res, ElementDefinition ed, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { + private Collection intersectTypes(ProfileComparison comp, StructuralMatch res, ElementDefinition ed, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { List result = new ArrayList(); for (TypeRefComponent l : left) { if (l.hasAggregation()) @@ -665,7 +711,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return b.toString(); } - private boolean compareBindings(ProfileComparison comp, StructuralMatch res, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef) throws FHIRFormatError, DefinitionException, IOException { + private boolean compareBindings(ProfileComparison comp, StructuralMatch res, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef) throws FHIRFormatError, DefinitionException, IOException { assert(lDef.hasBinding() || rDef.hasBinding()); if (!lDef.hasBinding()) { subset.setBinding(rDef.getBinding()); @@ -801,7 +847,7 @@ public class ProfileComparer extends CanonicalResourceComparer { } // we can't really know about constraints. We create warnings, and collate them - private List unionConstraints(ProfileComparison comp, StructuralMatch res, String path, List left, List right) { + private List unionConstraints(ProfileComparison comp, StructuralMatch res, String path, List left, List right) { List result = new ArrayList(); for (ElementDefinitionConstraintComponent l : left) { boolean found = false; @@ -829,7 +875,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return result; } - private StructureDefinition resolveProfile(ProfileComparison comp, StructuralMatch res, String path, String url, String name, IWorkerContext ctxt) { + private StructureDefinition resolveProfile(ProfileComparison comp, StructuralMatch res, String path, String url, String name, IWorkerContext ctxt) { StructureDefinition sd = ctxt.fetchResource(StructureDefinition.class, url); if (sd == null) { ValidationMessage vm = vmI(IssueSeverity.WARNING, "Unable to resolve profile "+url+" in profile "+name, path); @@ -841,7 +887,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return binding.getStrength() == BindingStrength.EXAMPLE || binding.getStrength() == BindingStrength.PREFERRED; } - private ElementDefinitionBindingComponent unionBindings(ProfileComparison comp, StructuralMatch res, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right) throws FHIRFormatError, DefinitionException, IOException { + private ElementDefinitionBindingComponent unionBindings(ProfileComparison comp, StructuralMatch res, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right) throws FHIRFormatError, DefinitionException, IOException { ElementDefinitionBindingComponent union = new ElementDefinitionBindingComponent(); if (left.getStrength().compareTo(right.getStrength()) < 0) union.setStrength(left.getStrength()); @@ -881,17 +927,17 @@ public class ProfileComparer extends CanonicalResourceComparer { return gen.generate(model, prefix, 0, null); } - private void genElementComp(String defPath, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { + private void genElementComp(String defPath, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { Row originalRow = slicingRow; Row typesRow = null; - List> children = combined.getChildren(); + List> children = combined.getChildren(); Row row = gen.new Row(); rows.add(row); - String path = combined.either().getPath(); + String path = combined.either().getDef().getPath(); row.setAnchor(path); - row.setColor(utilsRight.getRowColor(combined.either(), false)); + row.setColor(utilsRight.getRowColor(combined.either().getDef(), false)); if (eitherHasSlicing(combined)) row.setLineColor(1); else if (eitherHasSliceName(combined)) @@ -917,7 +963,7 @@ public class ProfileComparer extends CanonicalResourceComparer { row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); typesRow = row; } - } else if (combined.either().hasContentReference()) + } else if (combined.either().getDef().hasContentReference()) row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); else if (isPrimitive(combined)) row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); @@ -927,7 +973,7 @@ public class ProfileComparer extends CanonicalResourceComparer { row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); else row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - String ref = defPath == null ? null : defPath + combined.either().getId(); + String ref = defPath == null ? null : defPath + combined.either().getDef().getId(); String sName = tail(path); String sn = getSliceName(combined); if (sn != null) @@ -937,23 +983,23 @@ public class ProfileComparer extends CanonicalResourceComparer { String leftColor = !combined.hasLeft() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; String rightColor = !combined.hasRight() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; if (combined.hasLeft()) { - nc = utilsRight.genElementNameCell(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName); + nc = utilsRight.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName); } else { - nc = utilsRight.genElementNameCell(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName); + nc = utilsRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName); } if (combined.hasLeft()) { - frame(utilsRight.genElementCells(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName, nc, false), leftColor); + frame(utilsRight.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false), leftColor); } else { frame(spacers(row, 4, gen), leftColor); } if (combined.hasRight()) { - frame(utilsRight.genElementCells(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used, ref, sName, nc, false), rightColor); + frame(utilsRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false), rightColor); } else { frame(spacers(row, 4, gen), rightColor); } row.getCells().add(cellForMessages(gen, combined.getMessages())); - for (StructuralMatch child : children) { + for (StructuralMatch child : children) { genElementComp(defPath, gen, row.getSubRows(), child, corePath, prefix, originalRow, false); } } @@ -978,47 +1024,47 @@ public class ProfileComparer extends CanonicalResourceComparer { return res; } - private String getSliceName(StructuralMatch combined) { + private String getSliceName(StructuralMatch combined) { // TODO Auto-generated method stub return null; } - private boolean isDataType(StructuralMatch combined) { + private boolean isDataType(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean hasTarget(StructuralMatch combined) { + private boolean hasTarget(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean isPrimitive(StructuralMatch combined) { + private boolean isPrimitive(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean allAreReference(StructuralMatch combined) { + private boolean allAreReference(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean hasChoice(StructuralMatch combined) { + private boolean hasChoice(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean elementIsComplex(StructuralMatch combined) { + private boolean elementIsComplex(StructuralMatch combined) { // TODO Auto-generated method stub velement.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue() return false; } - private boolean eitherHasSliceName(StructuralMatch combined) { + private boolean eitherHasSliceName(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean eitherHasSlicing(StructuralMatch combined) { + private boolean eitherHasSlicing(StructuralMatch combined) { // TODO Auto-generated method stub return false; } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java index 0bcc84118..d8e13d43b 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java @@ -549,7 +549,17 @@ public class ProfileUtilities extends TranslatingUtilities { } public List getChildList(StructureDefinition structure, ElementDefinition element) { - return getChildList(structure, element.getPath(), element.getId(), false); + if (element.hasContentReference()) { + ElementDefinition target = element; + for (ElementDefinition t : structure.getSnapshot().getElement()) { + if (t.getId().equals(element.getContentReference().substring(1))) { + target = t; + } + } + return getChildList(structure, target.getPath(), target.getId(), false); + } else { + return getChildList(structure, element.getPath(), element.getId(), false); + } } public void updateMaps(StructureDefinition base, StructureDefinition derived) throws DefinitionException { @@ -1077,6 +1087,7 @@ public class ProfileUtilities extends TranslatingUtilities { ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy()); outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc)); updateFromBase(outcome, currentBase); + updateConstraintSources(outcome, srcSD.getUrl()); markDerived(outcome); if (resultPathBase == null) resultPathBase = outcome.getPath(); @@ -1913,6 +1924,15 @@ public class ProfileUtilities extends TranslatingUtilities { return res; } + private void updateConstraintSources(ElementDefinition ed, String url) { + for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { + if (!c.hasSource()) { + c.setSource(url); + } + } + + } + private Set getListOfTypes(ElementDefinition e) { Set result = new HashSet<>(); for (TypeRefComponent t : e.getType()) { @@ -2087,6 +2107,8 @@ public class ProfileUtilities extends TranslatingUtilities { private void removeStatusExtensions(ElementDefinition outcome) { outcome.removeExtension(ToolingExtensions.EXT_FMM_LEVEL); + outcome.removeExtension(ToolingExtensions.EXT_FMM_SUPPORT); + outcome.removeExtension(ToolingExtensions.EXT_FMM_DERIVED); outcome.removeExtension(ToolingExtensions.EXT_STANDARDS_STATUS); outcome.removeExtension(ToolingExtensions.EXT_NORMATIVE_VERSION); outcome.removeExtension(ToolingExtensions.EXT_WORKGROUP); @@ -2406,6 +2428,11 @@ public class ProfileUtilities extends TranslatingUtilities { StructureDefinition sd = null; if (type.hasProfile()) { sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue()); + if (sd == null) { + if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) { + sd = xver.makeDefinition(type.getProfile().get(0).getValue()); + } + } if (sd == null) System.out.println("Failed to find referenced profile: " + type.getProfile()); } @@ -2475,19 +2502,19 @@ public class ProfileUtilities extends TranslatingUtilities { if (webUrl != null) { // also, must touch up the markdown if (element.hasDefinition()) - element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames)); + element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); if (element.hasComment()) - element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames)); + element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); if (element.hasRequirements()) - element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames)); + element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); if (element.hasMeaningWhenMissing()) - element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames)); + element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); } } return element; } - public static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set filenames) { + public static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set filenames, boolean processRelatives) { StringBuilder b = new StringBuilder(); int i = 0; while (i < markdown.length()) { @@ -2515,7 +2542,14 @@ public class ProfileUtilities extends TranslatingUtilities { } else { b.append("]("); // disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all? - // b.append(webUrl); + // re-enabled 11-Feb 2022 GDG - we do want to do this. At least, $assemble in davinci-dtr, where the markdown comes from the SDC IG, and an SDC local reference must be changed to point to SDC. in this case, it's called when generating snapshots + // added processRelatives parameter to deal with this (well, to try) + if (processRelatives && webUrl != null) { +// System.out.println("Making "+url+" relative to '"+webUrl+"'"); + b.append(webUrl); + } else { +// System.out.println("Not making "+url+" relative to '"+webUrl+"'"); + } i = i + 1; } } else @@ -2773,7 +2807,9 @@ public class ProfileUtilities extends TranslatingUtilities { profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue()) : null; if (profile != null) { ElementDefinition e = profile.getSnapshot().getElement().get(0); - base.setDefinition(e.getDefinition()); + String webroot = profile.getUserString("webroot"); + + base.setDefinition(processRelativeUrls(e.getDefinition(), webroot, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); base.setShort(e.getShort()); if (e.hasCommentElement()) base.setCommentElement(e.getCommentElement()); @@ -3502,7 +3538,7 @@ public class ProfileUtilities extends TranslatingUtilities { List types = e.getType(); if (!e.hasType()) { if (root) { // we'll use base instead of types then - StructureDefinition bsd = context.fetchResource(StructureDefinition.class, profile.getBaseDefinition()); + StructureDefinition bsd = profile == null ? null : context.fetchResource(StructureDefinition.class, profile.getBaseDefinition()); if (bsd != null) { if (bsd.hasUserData("path")) { c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getUserString("path")) ? bsd.getUserString("path") : imagePath +bsd.getUserString("path"), bsd.getName(), null)); @@ -3584,7 +3620,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); } - ref = pkp.getLinkForProfile(profile, p.getValue()); + ref = pkp == null ? null : pkp.getLinkForProfile(profile, p.getValue()); if (ref != null) { String[] parts = ref.split("\\|"); if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { @@ -3665,7 +3701,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - private String codeForAggregation(AggregationMode a) { + public static String codeForAggregation(AggregationMode a) { switch (a) { case BUNDLED : return "b"; case CONTAINED : return "c"; @@ -3674,7 +3710,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } - private String hintForAggregation(AggregationMode a) { + public static String hintForAggregation(AggregationMode a) { if (a != null) return a.getDefinition(); else @@ -3683,7 +3719,7 @@ public class ProfileUtilities extends TranslatingUtilities { private String checkPrepend(String corePath, String path) { - if (pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) + if (pkp != null && pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) return corePath+path; else return path; @@ -4055,7 +4091,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR)) sName = "@"+sName; Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName); - genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport); + genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true); if (element.hasSlicing()) { if (standardExtensionSlicing(element)) { used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile(); @@ -4183,7 +4219,7 @@ public class ProfileUtilities extends TranslatingUtilities { public List genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, - boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport) throws IOException { + boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows) throws IOException { List res = new ArrayList<>(); Cell gc = gen.new Cell(); row.getCells().add(gc); @@ -4209,7 +4245,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (extDefn == null) { res.add(genCardinality(gen, element, row, hasDef, used, null)); res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null))); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } else { String name = urltail(eurl); nameCell.getPieces().get(0).setText(name); @@ -4222,7 +4258,7 @@ public class ProfileUtilities extends TranslatingUtilities { else // if it's complex, we just call it nothing // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null))); - res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows)); } } else { res.add(genCardinality(gen, element, row, hasDef, used, null)); @@ -4230,7 +4266,7 @@ public class ProfileUtilities extends TranslatingUtilities { res.add(addCell(row, gen.new Cell())); else res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } } else { res.add(genCardinality(gen, element, row, hasDef, used, null)); @@ -4238,7 +4274,7 @@ public class ProfileUtilities extends TranslatingUtilities { res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); else res.add(addCell(row, gen.new Cell())); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } return res; } @@ -4544,11 +4580,11 @@ public class ProfileUtilities extends TranslatingUtilities { && element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE); } - private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException { - return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly); + private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException { + return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows); } - private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException { + private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException { Cell c = gen.new Cell(); row.getCells().add(c); @@ -4564,7 +4600,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } if (root) { - if (profile.getAbstract()) { + if (profile != null && profile.getAbstract()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.addPiece(gen.new Piece(null, "This is an abstract profile", null)); } @@ -4659,7 +4695,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (binding!=null && !binding.isEmpty()) { if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - BindingResolution br = pkp.resolveBinding(profile, binding, definition.getPath()); + BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding, definition.getPath()); c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); if (binding.hasStrength()) { @@ -4668,7 +4704,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null))); } if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { - br = pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath()); + br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath()); c.addPiece(gen.new Piece("br")); c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-maxvalueset.html", translate("sd.table", "Max Binding")+": ", "Max Value Set Extension").addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); @@ -4683,6 +4719,13 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(gen.new Piece(null, ": ", null)); c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()))); } + if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { + c.addPiece(gen.new Piece("br")); + c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Additional Bindings")+": ", null).addStyle("font-weight:bold"))); + for (Extension ext : binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { + renderAdditionalBinding(gen, c, ext); + } + } } for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { @@ -4704,7 +4747,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (definition.hasFixed()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold"))); - if (!useTableForFixedValues || definition.getFixed().isPrimitive()) { + if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) { String s = buildJson(definition.getFixed()); String link = null; if (Utilities.isAbsoluteUrl(s)) @@ -4712,7 +4755,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); } else { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen"))); - genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); + genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); } if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { Piece p = describeCoded(gen, definition.getFixed()); @@ -4722,7 +4765,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (definition.hasPattern()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold"))); - if (!useTableForFixedValues || definition.getPattern().isPrimitive()) + if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive()) c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); else { c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen"))); @@ -4762,6 +4805,30 @@ public class ProfileUtilities extends TranslatingUtilities { return c; } + private void renderAdditionalBinding(HierarchicalTableGenerator gen, Cell c, Extension ext) { + // 2 purpose value-set-link ([context]) {documentation} + String purpose = ext.getExtensionString("purpose"); + String valueSet = ext.getExtensionString("valueSet"); + String doco = ext.getExtensionString("documentation"); + //UsageContext usage = (ext.hasExtension("usage")) ? ext.getExtensionByUrl("usage").getValueUsageContext() : null; +// +// purpose: code - defines how the binding is used +// usage : UsageContext - defines the contexts in which this binding is used for it's purpose +// valueSet : canonical(ValueSet) +// documentation : markdown +// !! +// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); +// c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, gt(inv.getHumanElement()), null))); + + } + + private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) { + BindingResolution br = new BindingResolution(); + br.url = "http://none.none/none"; + br.display = "todo"; + return br; + } + private ElementDefinitionBindingComponent makeUnifiedBinding(ElementDefinitionBindingComponent binding, ElementDefinition element) { if (!element.hasUserData(DERIVATION_POINTER)) { return binding; diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/ToolingExtensions.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/ToolingExtensions.java index 29d921926..6e00efbf4 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/ToolingExtensions.java @@ -198,6 +198,9 @@ public class ToolingExtensions { public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement"; public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath"; public static final String EXT_VALUESET_SYSTEM = "http://hl7.org/fhir/StructureDefinition/valueset-system"; + public static final String EXT_EXPAND_RULES = "http://hl7.org/fhir/StructureDefinition/valueset-expand-rules"; + public static final String EXT_EXPAND_GROUP = "http://hl7.org/fhir/StructureDefinition/valueset-expand-group"; + public static final String EXT_BINDING_ADDITIONAL = "http://hl7.org/fhir/tools/StructureDefinition/additional-binding"; // specific extension helpers @@ -511,7 +514,7 @@ public class ToolingExtensions { * @return The extension, if on this element, else null */ public static Extension getExtension(DomainResource resource, String name) { - if (name == null) + if (resource == null || name == null) return null; if (!resource.hasExtension()) return null; diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/XVerExtensionManager.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/XVerExtensionManager.java index 011725dbe..17d12ab29 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/XVerExtensionManager.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/XVerExtensionManager.java @@ -50,9 +50,6 @@ public class XVerExtensionManager { return XVerExtensionStatus.Invalid; } String v = url.substring(20, 23); - if ("5.0".equals(v)) { - v = "4.6"; // for now - } String e = url.substring(54); if (!lists.containsKey(v)) { if (context.getBinaries().containsKey("xver-paths-"+v+".json")) { @@ -67,6 +64,9 @@ public class XVerExtensionManager { } JsonObject root = lists.get(v); JsonObject path = root.getAsJsonObject(e); + if (path == null) { + path = root.getAsJsonObject(e+"[x]"); + } if (path == null) { return XVerExtensionStatus.Unknown; } @@ -83,13 +83,13 @@ public class XVerExtensionManager { public StructureDefinition makeDefinition(String url) { String verSource = url.substring(20, 23); - if ("5.0".equals(verSource)) { - verSource = "4.6"; // for now - } String verTarget = VersionUtilities.getMajMin(context.getVersion()); String e = url.substring(54); JsonObject root = lists.get(verSource); JsonObject path = root.getAsJsonObject(e); + if (path == null) { + path = root.getAsJsonObject(e+"[x]"); + } StructureDefinition sd = new StructureDefinition(); sd.setUserData(XVER_EXT_MARKER, "true"); @@ -117,16 +117,18 @@ public class XVerExtensionManager { populateTypes(path, val, verSource, verTarget); } else if (path.has("elements")) { for (JsonElement i : path.getAsJsonArray("elements")) { - String s = i.getAsString(); + JsonObject elt = root.getAsJsonObject(e+"."+i.getAsString()); + if (elt != null) { + String s = i.getAsString().replace("[x]", ""); sd.getDifferential().addElement().setPath("Extension.extension").setSliceName(s); sd.getDifferential().addElement().setPath("Extension.extension.extension").setMax("0"); sd.getDifferential().addElement().setPath("Extension.extension.url").setFixed(new UriType(s)); ElementDefinition val = sd.getDifferential().addElement().setPath("Extension.extension.value[x]").setMin(1); - JsonObject elt = root.getAsJsonObject(e+"."+s); if (!elt.has("types")) { throw new FHIRException("Internal error - nested elements not supported yet"); } populateTypes(elt, val, verSource, verTarget); + } } sd.getDifferential().addElement().setPath("Extension.url").setFixed(new UriType(url)); sd.getDifferential().addElement().setPath("Extension.value[x]").setMax("0"); diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/ProfileUtilitiesTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/ProfileUtilitiesTests.java index 930fcbb3b..d23e585e7 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/ProfileUtilitiesTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/ProfileUtilitiesTests.java @@ -4,6 +4,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4b.conformance.ProfileUtilities; import org.hl7.fhir.r4b.model.Base; import org.hl7.fhir.r4b.model.ElementDefinition; +import org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r4b.model.IntegerType; import org.hl7.fhir.r4b.model.StructureDefinition; import org.hl7.fhir.r4b.model.StructureDefinition.TypeDerivationRule; @@ -51,6 +52,12 @@ public class ProfileUtilitiesTests { f.setBase(null); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } ok = Base.compareDeep(b, f, true); } } @@ -83,6 +90,12 @@ public class ProfileUtilitiesTests { if (ok) { ElementDefinition b = base.getSnapshot().getElement().get(i); ElementDefinition f = focus.getSnapshot().getElement().get(i); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) ok = false; else { @@ -128,6 +141,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } @@ -174,6 +193,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } @@ -221,6 +246,12 @@ public class ProfileUtilitiesTests { ElementDefinition f = focus.getSnapshot().getElement().get(i); b.setRequirements(null); f.setRequirements(null); + for (ElementDefinitionConstraintComponent c : b.getConstraint()) { + c.setSource(null); + } + for (ElementDefinitionConstraintComponent c : f.getConstraint()) { + c.setSource(null); + } if (!f.hasBase() || !b.getPath().equals(f.getPath())) { ok = false; } diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java index 5fc5ab7a9..0141e129a 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.stream.Stream; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.soap.Text; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.NotImplementedException; @@ -49,6 +50,7 @@ import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xml.XMLUtil; +import org.junit.Assert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; @@ -137,7 +139,8 @@ public class SnapShotGenerationTests { private boolean fail; private boolean newSliceProcessing; private boolean debug; - + private boolean noR4b; + private List rules = new ArrayList<>(); private StructureDefinition source; private List included = new ArrayList<>(); @@ -161,6 +164,7 @@ public class SnapShotGenerationTests { rules.add(new Rule(rule)); rule = XMLUtil.getNextSibling(rule); } + noR4b = test.hasAttribute("r4b") && "false".equals(test.getAttribute("r4b")); } public String getId() { @@ -466,33 +470,37 @@ public class SnapShotGenerationTests { fp.setHostServices(context); messages = new ArrayList(); - System.out.println("---- "+id+" -----------------------------------------"); - if (test.isFail()) { - boolean failed = true; - try { - if (test.isGen()) - testGen(true, test, context); - else - testSort(test, context); - failed = false; - } catch (Throwable e) { - System.out.println("Error running test: " + e.getMessage()); - if (!Utilities.noString(test.regex)) { - Assertions.assertTrue(e.getMessage().matches(test.regex), "correct error message"); - } else if ("Should have failed".equals(e.getMessage())) { - throw e; - } else { + if (test.noR4b) { + Assert.assertTrue(true); + } else { + System.out.println("---- "+id+" -----------------------------------------"); + if (test.isFail()) { + boolean failed = true; + try { + if (test.isGen()) + testGen(true, test, context); + else + testSort(test, context); + failed = false; + } catch (Throwable e) { + System.out.println("Error running test: " + e.getMessage()); + if (!Utilities.noString(test.regex)) { + Assertions.assertTrue(e.getMessage().matches(test.regex), "correct error message"); + } else if ("Should have failed".equals(e.getMessage())) { + throw e; + } else { + } } + Assertions.assertTrue(failed, "Should have failed"); + } else if (test.isGen()) + testGen(false, test, context); + else + testSort(test, context); + for (Rule r : test.getRules()) { + StructureDefinition sdn = new StructureDefinition(); + boolean ok = fp.evaluateToBoolean(sdn, sdn, sdn, r.expression); + Assertions.assertTrue(ok, r.description); } - Assertions.assertTrue(failed, "Should have failed"); - } else if (test.isGen()) - testGen(false, test, context); - else - testSort(test, context); - for (Rule r : test.getRules()) { - StructureDefinition sdn = new StructureDefinition(); - boolean ok = fp.evaluateToBoolean(sdn, sdn, sdn, r.expression); - Assertions.assertTrue(ok, r.description); } } From 791f16a05da1427beb38d0eaaf6c5943c47fe08a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 10 Mar 2022 11:18:03 +1100 Subject: [PATCH 08/11] fix failing tests. --- .../fhir/convertors/misc/JsonProcessor.java | 189 +++++++++++ .../fhir/r5/conformance/ProfileUtilities.java | 311 +++++++++--------- .../hl7/fhir/utilities/tests/RegexTests.java | 25 ++ pom.xml | 2 +- 4 files changed, 373 insertions(+), 154 deletions(-) create mode 100644 org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/JsonProcessor.java create mode 100644 org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/RegexTests.java diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/JsonProcessor.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/JsonProcessor.java new file mode 100644 index 000000000..a46bd5c5b --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/JsonProcessor.java @@ -0,0 +1,189 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.File; +import java.io.IOException; + +import org.hl7.fhir.utilities.json.JsonTrackingParser; + +import com.google.gson.JsonObject; + +public class JsonProcessor { + public static void main(String[] args) throws Exception { + new JsonProcessor().process(args[0]); + } + + private void process(String source) throws IOException { + JsonObject json = JsonTrackingParser.parseJsonFile(source); + process(json); + JsonTrackingParser.write(json, new File(source), true); + + } + + private void process(JsonObject json) { + process(json, "ActivityDefinition.status"); + process(json, "CapabilityStatement.status"); + process(json, "CodeSystem.status"); + process(json, "CompartmentDefinition.status"); + process(json, "ConceptMap.status"); + process(json, "DataElement.status"); + process(json, "ExpansionProfile.status"); + process(json, "GraphDefinition.status"); + process(json, "ImplementationGuide.status"); + process(json, "Library.status"); + process(json, "Measure.status"); + process(json, "MessageDefinition.status"); + process(json, "OperationDefinition.status"); + process(json, "PlanDefinition.status"); + process(json, "Questionnaire.status"); + process(json, "SearchParameter.status"); + process(json, "ServiceDefinition.status"); + process(json, "StructureDefinition.status"); + process(json, "StructureMap.status"); + process(json, "TestScript.status"); + process(json, "ValueSet.status"); + process(json, "ActivityDefinition.experimental"); + process(json, "CapabilityStatement.experimental"); + process(json, "CodeSystem.experimental"); + process(json, "CompartmentDefinition.experimental"); + process(json, "ConceptMap.experimental"); + process(json, "DataElement.experimental"); + process(json, "ExpansionProfile.experimental"); + process(json, "GraphDefinition.experimental"); + process(json, "ImplementationGuide.experimental"); + process(json, "Library.experimental"); + process(json, "Measure.experimental"); + process(json, "MessageDefinition.experimental"); + process(json, "OperationDefinition.experimental"); + process(json, "PlanDefinition.experimental"); + process(json, "Questionnaire.experimental"); + process(json, "SearchParameter.experimental"); + process(json, "ServiceDefinition.experimental"); + process(json, "StructureDefinition.experimental"); + process(json, "StructureMap.experimental"); + process(json, "TestScript.experimental"); + process(json, "ValueSet.experimental"); + process(json, "Identifier.use"); + process(json, "Quantity.comparator"); + process(json, "Address.use"); + process(json, "ContactPoint.use"); + process(json, "HumanName.use"); + process(json, "BackboneElement.modifierExtension"); + process(json, "DomainResource.modifierExtension"); + process(json, "Resource.implicitRules"); + process(json, "Account.status"); + process(json, "AllergyIntolerance.clinicalStatus"); + process(json, "AllergyIntolerance.verificationStatus"); + process(json, "Appointment.status"); + process(json, "AppointmentResponse.participantStatus"); + process(json, "Basic.code"); + process(json, "BodySite.active"); + process(json, "CarePlan.status"); + process(json, "CarePlan.intent"); + process(json, "CarePlan.activity.detail.status"); + process(json, "CarePlan.activity.detail.prohibited"); + process(json, "CareTeam.status"); + process(json, "ChargeItem.status"); + process(json, "Claim.status"); + process(json, "ClaimResponse.status"); + process(json, "ClinicalImpression.status"); + process(json, "Communication.status"); + process(json, "Communication.notDone"); + process(json, "CommunicationRequest.status"); + process(json, "Composition.status"); + process(json, "Composition.confidentiality"); + process(json, "Composition.section.mode"); + process(json, "ConceptMap.group.element.target.equivalence"); + process(json, "Condition.clinicalStatus"); + process(json, "Condition.verificationStatus"); + process(json, "Consent.status"); + process(json, "Contract.status"); + process(json, "Coverage.status"); + process(json, "DetectedIssue.status"); + process(json, "Device.status"); + process(json, "DeviceRequest.status"); + process(json, "DeviceRequest.intent"); + process(json, "DeviceUseStatement.status"); + process(json, "DiagnosticReport.status"); + process(json, "DocumentManifest.status"); + process(json, "DocumentReference.status"); + process(json, "DocumentReference.relatesTo"); + process(json, "EligibilityRequest.status"); + process(json, "EligibilityResponse.status"); + process(json, "Encounter.status"); + process(json, "Endpoint.status"); + process(json, "EnrollmentRequest.status"); + process(json, "EnrollmentResponse.status"); + process(json, "EpisodeOfCare.status"); + process(json, "ExplanationOfBenefit.status"); + process(json, "FamilyMemberHistory.status"); + process(json, "FamilyMemberHistory.notDone"); + process(json, "FamilyMemberHistory.estimatedAge"); + process(json, "Flag.status"); + process(json, "Goal.status"); + process(json, "Group.characteristic.exclude"); + process(json, "GuidanceResponse.status"); + process(json, "HealthcareService.active"); + process(json, "Immunization.status"); + process(json, "Immunization.notGiven"); + process(json, "List.status"); + process(json, "List.mode"); + process(json, "List.entry.deleted"); + process(json, "Location.status"); + process(json, "Location.mode"); + process(json, "MeasureReport.status"); + process(json, "MedicationAdministration.status"); + process(json, "MedicationAdministration.notGiven"); + process(json, "MedicationDispense.status"); + process(json, "MedicationRequest.status"); + process(json, "MedicationRequest.intent"); + process(json, "MedicationRequest.substitution.allowed"); + process(json, "MedicationStatement.status"); + process(json, "MedicationStatement.taken"); + process(json, "NamingSystem.status"); + process(json, "NutritionOrder.status"); + process(json, "Observation.status"); + process(json, "OperationOutcome.issue.severity"); + process(json, "Organization.active"); + process(json, "Patient.active"); + process(json, "Patient.deceased[x]"); + process(json, "Patient.animal"); + process(json, "Patient.link"); + process(json, "PaymentNotice.status"); + process(json, "PaymentReconciliation.status"); + process(json, "Person.active"); + process(json, "Procedure.status"); + process(json, "Procedure.notDone"); + process(json, "ProcedureRequest.status"); + process(json, "ProcedureRequest.intent"); + process(json, "ProcedureRequest.doNotPerform"); + process(json, "ProcessRequest.status"); + process(json, "ProcessResponse.status"); + process(json, "Questionnaire.item.enableWhen"); + process(json, "QuestionnaireResponse.status"); + process(json, "ReferralRequest.status"); + process(json, "ReferralRequest.intent"); + process(json, "RelatedPerson.active"); + process(json, "RequestGroup.status"); + process(json, "RequestGroup.intent"); + process(json, "ResearchStudy.status"); + process(json, "ResearchSubject.status"); + process(json, "Schedule.active"); + process(json, "Specimen.status"); + process(json, "Subscription.status"); + process(json, "SupplyDelivery.status"); + process(json, "SupplyRequest.status"); + process(json, "TestReport.status"); + process(json, "ValueSet.compose.include.filter"); + process(json, "VisionPrescription.status"); + } + + private void process(JsonObject json, String name) { + JsonObject j = json.getAsJsonObject(name); + if (j == null) { + System.out.println("Can't find "+name); + } else { + j.addProperty("modifier", true); + } + + } +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 8e9e0bf51..ec0032ca8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -621,180 +621,184 @@ public class ProfileUtilities extends TranslatingUtilities { if (!base.getType().equals(derived.getType()) && derived.getDerivation() == TypeDerivationRule.CONSTRAINT) { throw new DefinitionException(context.formatMessage(I18nConstants.BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___, base.getUrl(), base.getType(), derived.getUrl(), derived.getType())); } - + if (snapshotStack.contains(derived.getUrl())) { throw new DefinitionException(context.formatMessage(I18nConstants.CIRCULAR_SNAPSHOT_REFERENCES_DETECTED_CANNOT_GENERATE_SNAPSHOT_STACK__, snapshotStack.toString())); } derived.setUserData("profileutils.snapshot.generating", true); snapshotStack.add(derived.getUrl()); - - if (!Utilities.noString(webUrl) && !webUrl.endsWith("/")) - webUrl = webUrl + '/'; - - if (defWebRoot == null) - defWebRoot = webUrl; - derived.setSnapshot(new StructureDefinitionSnapshotComponent()); - try { - checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl()); - checkDifferentialBaseType(derived); - - // so we have two lists - the base list, and the differential list - // the differential list is only allowed to include things that are in the base list, but - // is allowed to include them multiple times - thereby slicing them - // our approach is to walk through the base list, and see whether the differential - // says anything about them. - int baseCursor = 0; - int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths + if (!Utilities.noString(webUrl) && !webUrl.endsWith("/")) + webUrl = webUrl + '/'; + + if (defWebRoot == null) + defWebRoot = webUrl; + derived.setSnapshot(new StructureDefinitionSnapshotComponent()); + + try { + checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl()); + checkDifferentialBaseType(derived); + + // so we have two lists - the base list, and the differential list + // the differential list is only allowed to include things that are in the base list, but + // is allowed to include them multiple times - thereby slicing them + + // our approach is to walk through the base list, and see whether the differential + // says anything about them. + int baseCursor = 0; + int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths - for (ElementDefinition e : derived.getDifferential().getElement()) - e.clearUserData(GENERATED_IN_SNAPSHOT); + for (ElementDefinition e : derived.getDifferential().getElement()) + e.clearUserData(GENERATED_IN_SNAPSHOT); - // we actually delegate the work to a subroutine so we can re-enter it with a different cursors - StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards - StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot(); - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - String derivedType = derived.getType(); - if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) { - derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1); + // we actually delegate the work to a subroutine so we can re-enter it with a different cursors + StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards + StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot(); + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + String derivedType = derived.getType(); + if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) { + derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1); + } + baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType); } - baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType); - } -// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) { -// debug = true; -// } - processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1, - derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList(), base); - checkGroupConstraints(derived); - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition e : diff.getElement()) { - if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { - ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); - e.setUserData(GENERATED_IN_SNAPSHOT, outcome); - derived.getSnapshot().addElement(outcome); + // if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) { + // debug = true; + // } + processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1, + derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList(), base); + checkGroupConstraints(derived); + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition e : diff.getElement()) { + if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { + ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); + e.setUserData(GENERATED_IN_SNAPSHOT, outcome); + derived.getSnapshot().addElement(outcome); + } } } - } - - if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty()) - throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl())); - updateMaps(base, derived); - setIds(derived, false); - if (debug) { - System.out.println("Differential: "); - for (ElementDefinition ed : derived.getDifferential().getElement()) - System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); - System.out.println("Snapshot: "); - for (ElementDefinition ed : derived.getSnapshot().getElement()) - System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); - } - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - //Check that all differential elements have a corresponding snapshot element - int ce = 0; - for (ElementDefinition e : diff.getElement()) { - if (!e.hasUserData("diff-source")) - throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT)); - else { - if (e.hasUserData(DERIVATION_EQUALS)) - ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS)); - if (e.hasUserData(DERIVATION_POINTER)) - ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER)); - } - if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { - b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath()); - ce++; - if (e.hasId()) { - String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)"; - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR)); - } - } - } - if (!Utilities.noString(b.toString())) { - String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)"; - System.out.println("Error in snapshot generation: "+msg); - if (!debug) { + if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty()) + throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl())); + updateMaps(base, derived); + + setIds(derived, false); + if (debug) { System.out.println("Differential: "); for (ElementDefinition ed : derived.getDifferential().getElement()) - System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); System.out.println("Snapshot: "); for (ElementDefinition ed : derived.getSnapshot().getElement()) - System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); } - if (exception) - throw new DefinitionException(msg); - else - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); - } - // hack around a problem in R4 definitions (somewhere?) - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - for (ElementDefinitionMappingComponent mm : ed.getMapping()) { - if (mm.hasMap()) { - mm.setMap(mm.getMap().trim()); + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + //Check that all differential elements have a corresponding snapshot element + int ce = 0; + for (ElementDefinition e : diff.getElement()) { + if (!e.hasUserData("diff-source")) + throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT)); + else { + if (e.hasUserData(DERIVATION_EQUALS)) + ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS)); + if (e.hasUserData(DERIVATION_POINTER)) + ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER)); } - } - for (ElementDefinitionConstraintComponent s : ed.getConstraint()) { - if (s.hasSource()) { - String ref = s.getSource(); - if (!Utilities.isAbsoluteUrl(ref)) { - if (ref.contains(".")) { - s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref); - } else { - s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref); - } - } - } - } - } - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - if (!ed.hasBase()) { - ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); - } - } - } - // last, check for wrong profiles or target profiles - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - for (TypeRefComponent t : ed.getType()) { - for (UriType u : t.getProfile()) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); - if (sd == null) { - if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) { - sd = xver.makeDefinition(u.getValue()); - } + if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { + b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath()); + ce++; + if (e.hasId()) { + String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)"; + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR)); } - if (sd == null) { - if (messages != null) { - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING)); + } + } + if (!Utilities.noString(b.toString())) { + String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)"; + System.out.println("Error in snapshot generation: "+msg); + if (!debug) { + System.out.println("Differential: "); + for (ElementDefinition ed : derived.getDifferential().getElement()) + System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println("Snapshot: "); + for (ElementDefinition ed : derived.getSnapshot().getElement()) + System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + } + if (exception) + throw new DefinitionException(msg); + else + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); + } + // hack around a problem in R4 definitions (somewhere?) + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (ElementDefinitionMappingComponent mm : ed.getMapping()) { + if (mm.hasMap()) { + mm.setMap(mm.getMap().trim()); + } + } + for (ElementDefinitionConstraintComponent s : ed.getConstraint()) { + if (s.hasSource()) { + String ref = s.getSource(); + if (!Utilities.isAbsoluteUrl(ref)) { + if (ref.contains(".")) { + s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref); + } else { + s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref); + } + } + } + } + } + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + if (!ed.hasBase()) { + ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); + } + } + } + // last, check for wrong profiles or target profiles + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (TypeRefComponent t : ed.getType()) { + for (UriType u : t.getProfile()) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); + if (sd == null) { + if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) { + sd = xver.makeDefinition(u.getValue()); + } } - } else { - String wt = t.getWorkingCode(); - if (ed.getPath().equals("Bundle.entry.response.outcome")) { - wt = "OperationOutcome"; - } - if (!sd.getType().equals(wt)) { - boolean ok = isCompatibleType(wt, sd); - if (!ok) { - String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt; - if (exception) - throw new DefinitionException(smsg); - else - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR)); + if (sd == null) { + if (messages != null) { + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING)); + } + } else { + String wt = t.getWorkingCode(); + if (ed.getPath().equals("Bundle.entry.response.outcome")) { + wt = "OperationOutcome"; + } + if (!sd.getType().equals(wt)) { + boolean ok = isCompatibleType(wt, sd); + if (!ok) { + String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt; + if (exception) + throw new DefinitionException(smsg); + else + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR)); + } } } } } } + } catch (Exception e) { + // if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind + derived.setSnapshot(null); + derived.clearUserData("profileutils.snapshot.generating"); + throw e; } - } catch (Exception e) { - // if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind - derived.setSnapshot(null); + } finally { derived.clearUserData("profileutils.snapshot.generating"); - throw e; + snapshotStack.remove(derived.getUrl()); } - derived.clearUserData("profileutils.snapshot.generating"); } public void checkDifferentialBaseType(StructureDefinition derived) throws Error { @@ -1147,7 +1151,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, nbc, start, nbl-1, diffCursor-1, url, webUrl, profileName, tgt.getElement().getPath(), outcome.getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); } } else { - StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0), webUrl); if (dt == null) { throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), cpath)); } @@ -1300,7 +1304,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getElement().getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); } } else { - StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element"); + StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0), webUrl) : getProfileForDataType("Element"); if (dt == null) throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName)); contextName = dt.getUrl(); @@ -1511,7 +1515,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (baseHasChildren(base, currentBase)) { // not a new type here throw new Error("This situation is not yet handled (constrain slicing to 1..1 and fix base slice for inline structure - please report issue to grahame@fhir.org along with a test case that reproduces this error (@ "+cpath+" | "+currentBase.getPath()+")"); } else { - StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome); + StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl); contextName = dt.getUrl(); diffCursor++; start = diffCursor; @@ -1577,7 +1581,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, baseCursor+1, diffCursor, baseLimit, diffLimit, url, webUrl, profileName, contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD); baseCursor = indexOfFirstNonChild(base, currentBase, baseCursor, baseLimit); } else { - StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome); + StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl); contextName = dt.getUrl(); int start = diffCursor; while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath+".")) @@ -1778,7 +1782,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (base.getElement().get(baseCursor).getType().size() != 1) { throw new Error(context.formatMessage(I18nConstants.DIFFERENTIAL_WALKS_INTO____BUT_THE_BASE_DOES_NOT_AND_THERE_IS_NOT_A_SINGLE_FIXED_TYPE_THE_TYPE_IS__THIS_IS_NOT_HANDLED_YET, cpath, diffMatches.get(0).toString(), base.getElement().get(baseCursor).typeSummary())); } - StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0)); + StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0), webUrl); if (dt == null) { throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath())); } @@ -1894,7 +1898,7 @@ public class ProfileUtilities extends TranslatingUtilities { diffCursor - 1, url, webUrl, profileName+pathTail(diffMatches, 0), base.getElement().get(0).getPath(), base.getElement().get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD); } else { - StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl); // if (t.getCode().equals("Extension") && t.hasProfile() && !t.getProfile().contains(":")) { // lloydfix dt = // } @@ -1946,7 +1950,7 @@ public class ProfileUtilities extends TranslatingUtilities { } public StructureDefinition getTypeForElement(StructureDefinitionDifferentialComponent differential, int diffCursor, String profileName, - List diffMatches, ElementDefinition outcome) { + List diffMatches, ElementDefinition outcome, String webUrl) { if (outcome.getType().size() == 0) { throw new DefinitionException(context.formatMessage(I18nConstants._HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), profileName)); } @@ -1956,7 +1960,7 @@ public class ProfileUtilities extends TranslatingUtilities { throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName)); } } - StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl); if (dt == null) throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath())); return dt; @@ -2428,13 +2432,14 @@ public class ProfileUtilities extends TranslatingUtilities { return s; } - private StructureDefinition getProfileForDataType(TypeRefComponent type) { + private StructureDefinition getProfileForDataType(TypeRefComponent type, String webUrl) { StructureDefinition sd = null; if (type.hasProfile()) { sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue()); if (sd == null) { if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) { sd = xver.makeDefinition(type.getProfile().get(0).getValue()); + generateSnapshot(context.fetchTypeDefinition("Extension"), sd, sd.getUrl(), webUrl, sd.getName()); } } if (sd == null) diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/RegexTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/RegexTests.java new file mode 100644 index 000000000..f7f78d999 --- /dev/null +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/RegexTests.java @@ -0,0 +1,25 @@ +package org.hl7.fhir.utilities.tests; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RegexTests { + + @Test + public void testPath1() throws IOException { + Assertions.assertFalse("http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1".matches("Library")); + } + + @Test + public void testPath2() throws IOException { + Assertions.assertTrue("http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1".matches(".*Library.*")); + } + + @Test + public void testPath3() throws IOException { + Assertions.assertTrue("http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1".matches("(?s).*Library.*")); + } + +} diff --git a/pom.xml b/pom.xml index dbb6f8c5a..2740cbe0e 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.4.0 - 1.1.92 + 1.1.93-SNAPSHOT 5.7.1 1.7.1 3.0.0-M5 From 7a7579628cc7fc08884e73449bba035fb0ae9836 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 10 Mar 2022 11:40:47 +1100 Subject: [PATCH 09/11] compile fix From 21af65d9d66604c56956015211ec3b2bf18e190e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Mar 2022 06:42:28 +1100 Subject: [PATCH 10/11] get tests passing for R5 changes From 3f9d651b9b607e9c61056f3c85871ed17cd735d2 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Mar 2022 06:43:12 +1100 Subject: [PATCH 11/11] updates for r5 changes --- .../r4b/conformance/ProfileUtilities.java | 313 +++++++++--------- .../r4b/test/SnapShotGenerationTests.java | 1 - 2 files changed, 159 insertions(+), 155 deletions(-) diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java index d8e13d43b..3baefc805 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/conformance/ProfileUtilities.java @@ -623,174 +623,178 @@ public class ProfileUtilities extends TranslatingUtilities { } derived.setUserData("profileutils.snapshot.generating", true); snapshotStack.add(derived.getUrl()); - - if (!Utilities.noString(webUrl) && !webUrl.endsWith("/")) - webUrl = webUrl + '/'; - - if (defWebRoot == null) - defWebRoot = webUrl; - derived.setSnapshot(new StructureDefinitionSnapshotComponent()); - try { - checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl()); - checkDifferentialBaseType(derived); - - // so we have two lists - the base list, and the differential list - // the differential list is only allowed to include things that are in the base list, but - // is allowed to include them multiple times - thereby slicing them - // our approach is to walk through the base list, and see whether the differential - // says anything about them. - int baseCursor = 0; - int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths + if (!Utilities.noString(webUrl) && !webUrl.endsWith("/")) + webUrl = webUrl + '/'; + + if (defWebRoot == null) + defWebRoot = webUrl; + derived.setSnapshot(new StructureDefinitionSnapshotComponent()); + + try { + checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl()); + checkDifferentialBaseType(derived); + + // so we have two lists - the base list, and the differential list + // the differential list is only allowed to include things that are in the base list, but + // is allowed to include them multiple times - thereby slicing them + + // our approach is to walk through the base list, and see whether the differential + // says anything about them. + int baseCursor = 0; + int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths - for (ElementDefinition e : derived.getDifferential().getElement()) - e.clearUserData(GENERATED_IN_SNAPSHOT); + for (ElementDefinition e : derived.getDifferential().getElement()) + e.clearUserData(GENERATED_IN_SNAPSHOT); - // we actually delegate the work to a subroutine so we can re-enter it with a different cursors - StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards - StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot(); - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - String derivedType = derived.getType(); - if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) { - derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1); + // we actually delegate the work to a subroutine so we can re-enter it with a different cursors + StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards + StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot(); + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + String derivedType = derived.getType(); + if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) { + derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1); + } + baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType); } - baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType); - } -// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) { -// debug = true; -// } - processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1, - derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList(), base); - checkGroupConstraints(derived); - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition e : diff.getElement()) { - if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { - ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); - e.setUserData(GENERATED_IN_SNAPSHOT, outcome); - derived.getSnapshot().addElement(outcome); + // if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) { + // debug = true; + // } + processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1, + derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList(), base); + checkGroupConstraints(derived); + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition e : diff.getElement()) { + if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { + ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); + e.setUserData(GENERATED_IN_SNAPSHOT, outcome); + derived.getSnapshot().addElement(outcome); + } } } - } - - if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty()) - throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl())); - updateMaps(base, derived); - setIds(derived, false); - if (debug) { - System.out.println("Differential: "); - for (ElementDefinition ed : derived.getDifferential().getElement()) - System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); - System.out.println("Snapshot: "); - for (ElementDefinition ed : derived.getSnapshot().getElement()) - System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); - } - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - //Check that all differential elements have a corresponding snapshot element - int ce = 0; - for (ElementDefinition e : diff.getElement()) { - if (!e.hasUserData("diff-source")) - throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT)); - else { - if (e.hasUserData(DERIVATION_EQUALS)) - ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS)); - if (e.hasUserData(DERIVATION_POINTER)) - ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER)); - } - if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { - b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath()); - ce++; - if (e.hasId()) { - String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)"; - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR)); - } - } - } - if (!Utilities.noString(b.toString())) { - String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)"; - System.out.println("Error in snapshot generation: "+msg); - if (!debug) { + if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty()) + throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl())); + updateMaps(base, derived); + + setIds(derived, false); + if (debug) { System.out.println("Differential: "); for (ElementDefinition ed : derived.getDifferential().getElement()) - System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); System.out.println("Snapshot: "); for (ElementDefinition ed : derived.getSnapshot().getElement()) - System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println(" "+ed.getId()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); } - if (exception) - throw new DefinitionException(msg); - else - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); - } - // hack around a problem in R4 definitions (somewhere?) - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - for (ElementDefinitionMappingComponent mm : ed.getMapping()) { - if (mm.hasMap()) { - mm.setMap(mm.getMap().trim()); + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + //Check that all differential elements have a corresponding snapshot element + int ce = 0; + for (ElementDefinition e : diff.getElement()) { + if (!e.hasUserData("diff-source")) + throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT)); + else { + if (e.hasUserData(DERIVATION_EQUALS)) + ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS)); + if (e.hasUserData(DERIVATION_POINTER)) + ((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER)); } - } - for (ElementDefinitionConstraintComponent s : ed.getConstraint()) { - if (s.hasSource()) { - String ref = s.getSource(); - if (!Utilities.isAbsoluteUrl(ref)) { - if (ref.contains(".")) { - s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref); - } else { - s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref); - } - } - } - } - } - if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - if (!ed.hasBase()) { - ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); - } - } - } - // last, check for wrong profiles or target profiles - for (ElementDefinition ed : derived.getSnapshot().getElement()) { - for (TypeRefComponent t : ed.getType()) { - for (UriType u : t.getProfile()) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); - if (sd == null) { - if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) { - sd = xver.makeDefinition(u.getValue()); - } + if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { + b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath()); + ce++; + if (e.hasId()) { + String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)"; + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR)); } - if (sd == null) { - if (messages != null) { - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING)); + } + } + if (!Utilities.noString(b.toString())) { + String msg = "The profile "+derived.getUrl()+" has "+ce+" "+Utilities.pluralize("element", ce)+" in the differential ("+b.toString()+") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)"; + System.out.println("Error in snapshot generation: "+msg); + if (!debug) { + System.out.println("Differential: "); + for (ElementDefinition ed : derived.getDifferential().getElement()) + System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + System.out.println("Snapshot: "); + for (ElementDefinition ed : derived.getSnapshot().getElement()) + System.out.println(" "+ed.getId()+" = "+ed.getPath()+" : "+typeSummaryWithProfile(ed)+"["+ed.getMin()+".."+ed.getMax()+"]"+sliceSummary(ed)+" "+constraintSummary(ed)); + } + if (exception) + throw new DefinitionException(msg); + else + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); + } + // hack around a problem in R4 definitions (somewhere?) + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (ElementDefinitionMappingComponent mm : ed.getMapping()) { + if (mm.hasMap()) { + mm.setMap(mm.getMap().trim()); + } + } + for (ElementDefinitionConstraintComponent s : ed.getConstraint()) { + if (s.hasSource()) { + String ref = s.getSource(); + if (!Utilities.isAbsoluteUrl(ref)) { + if (ref.contains(".")) { + s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref.substring(0, ref.indexOf("."))+"#"+ref); + } else { + s.setSource("http://hl7.org/fhir/StructureDefinition/"+ref); + } + } + } + } + } + if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + if (!ed.hasBase()) { + ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); + } + } + } + // last, check for wrong profiles or target profiles + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (TypeRefComponent t : ed.getType()) { + for (UriType u : t.getProfile()) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); + if (sd == null) { + if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) { + sd = xver.makeDefinition(u.getValue()); + } } - } else { - String wt = t.getWorkingCode(); - if (ed.getPath().equals("Bundle.entry.response.outcome")) { - wt = "OperationOutcome"; - } - if (!sd.getType().equals(wt)) { - boolean ok = isCompatibleType(wt, sd); - if (!ok) { - String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt; - if (exception) - throw new DefinitionException(smsg); - else - messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR)); + if (sd == null) { + if (messages != null) { + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING)); + } + } else { + String wt = t.getWorkingCode(); + if (ed.getPath().equals("Bundle.entry.response.outcome")) { + wt = "OperationOutcome"; + } + if (!sd.getType().equals(wt)) { + boolean ok = isCompatibleType(wt, sd); + if (!ok) { + String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt; + if (exception) + throw new DefinitionException(smsg); + else + messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR)); + } } } } } } + } catch (Exception e) { + // if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind + derived.setSnapshot(null); + derived.clearUserData("profileutils.snapshot.generating"); + throw e; } - } catch (Exception e) { - // if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind - derived.setSnapshot(null); + } finally { derived.clearUserData("profileutils.snapshot.generating"); - throw e; + snapshotStack.remove(derived.getUrl()); } - derived.clearUserData("profileutils.snapshot.generating"); } public void checkDifferentialBaseType(StructureDefinition derived) throws Error { @@ -1143,7 +1147,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, nbc, start, nbl-1, diffCursor-1, url, webUrl, profileName, tgt.getElement().getPath(), outcome.getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); } } else { - StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = outcome.getType().size() > 1 ? context.fetchTypeDefinition("Element") : getProfileForDataType(outcome.getType().get(0), webUrl); if (dt == null) { throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), cpath)); } @@ -1296,7 +1300,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getElement().getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); } } else { - StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element"); + StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0), webUrl) : getProfileForDataType("Element"); if (dt == null) throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName)); contextName = dt.getUrl(); @@ -1507,7 +1511,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (baseHasChildren(base, currentBase)) { // not a new type here throw new Error("This situation is not yet handled (constrain slicing to 1..1 and fix base slice for inline structure - please report issue to grahame@fhir.org along with a test case that reproduces this error (@ "+cpath+" | "+currentBase.getPath()+")"); } else { - StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome); + StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl); contextName = dt.getUrl(); diffCursor++; start = diffCursor; @@ -1573,7 +1577,7 @@ public class ProfileUtilities extends TranslatingUtilities { processPaths(indent+" ", result, base, differential, baseCursor+1, diffCursor, baseLimit, diffLimit, url, webUrl, profileName, contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD); baseCursor = indexOfFirstNonChild(base, currentBase, baseCursor, baseLimit); } else { - StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome); + StructureDefinition dt = getTypeForElement(differential, diffCursor, profileName, diffMatches, outcome, webUrl); contextName = dt.getUrl(); int start = diffCursor; while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath+".")) @@ -1774,7 +1778,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (base.getElement().get(baseCursor).getType().size() != 1) { throw new Error(context.formatMessage(I18nConstants.DIFFERENTIAL_WALKS_INTO____BUT_THE_BASE_DOES_NOT_AND_THERE_IS_NOT_A_SINGLE_FIXED_TYPE_THE_TYPE_IS__THIS_IS_NOT_HANDLED_YET, cpath, diffMatches.get(0).toString(), base.getElement().get(baseCursor).typeSummary())); } - StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0)); + StructureDefinition dt = getProfileForDataType(base.getElement().get(baseCursor).getType().get(0), webUrl); if (dt == null) { throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath())); } @@ -1890,7 +1894,7 @@ public class ProfileUtilities extends TranslatingUtilities { diffCursor - 1, url, webUrl, profileName+pathTail(diffMatches, 0), base.getElement().get(0).getPath(), base.getElement().get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirector, srcSD); } else { - StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl); // if (t.getCode().equals("Extension") && t.hasProfile() && !t.getProfile().contains(":")) { // lloydfix dt = // } @@ -1942,7 +1946,7 @@ public class ProfileUtilities extends TranslatingUtilities { } public StructureDefinition getTypeForElement(StructureDefinitionDifferentialComponent differential, int diffCursor, String profileName, - List diffMatches, ElementDefinition outcome) { + List diffMatches, ElementDefinition outcome, String webUrl) { if (outcome.getType().size() == 0) { throw new DefinitionException(context.formatMessage(I18nConstants._HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), profileName)); } @@ -1952,7 +1956,7 @@ public class ProfileUtilities extends TranslatingUtilities { throw new DefinitionException(context.formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, diffMatches.get(0).getPath(), differential.getElement().get(diffCursor).getPath(), typeCode(outcome.getType()), profileName)); } } - StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); + StructureDefinition dt = getProfileForDataType(outcome.getType().get(0), webUrl); if (dt == null) throw new DefinitionException(context.formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath())); return dt; @@ -2424,13 +2428,14 @@ public class ProfileUtilities extends TranslatingUtilities { return s; } - private StructureDefinition getProfileForDataType(TypeRefComponent type) { + private StructureDefinition getProfileForDataType(TypeRefComponent type, String webUrl) { StructureDefinition sd = null; if (type.hasProfile()) { sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).getValue()); if (sd == null) { if (xver != null && xver.matchingUrl(type.getProfile().get(0).getValue()) && xver.status(type.getProfile().get(0).getValue()) == XVerExtensionStatus.Valid) { sd = xver.makeDefinition(type.getProfile().get(0).getValue()); + generateSnapshot(context.fetchTypeDefinition("Extension"), sd, sd.getUrl(), webUrl, sd.getName()); } } if (sd == null) @@ -4325,12 +4330,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean isBaseCondition(IdType c) { String key = c.asStringValue(); - return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); } private boolean isBaseConstraint(ElementDefinitionConstraintComponent con) { String key = con.getKey(); - return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + return key != null && key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); } private void makeChoiceRows(List subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode) { diff --git a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java index 0141e129a..64efbc82d 100644 --- a/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/test/SnapShotGenerationTests.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.stream.Stream; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.soap.Text; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.NotImplementedException;