From f5b65b54577a44aaba2fdd22083e32d256ce27ff Mon Sep 17 00:00:00 2001 From: Lloyd McKenzie Date: Thu, 21 Nov 2024 14:29:18 -0700 Subject: [PATCH] Update logic for determining key elements to include elements where min, max, minValue, maxValue, bindings, etc. have changed from the base resource. Needed for https://github.com/HL7/fhir-ig-publisher/issues/888 Also ensure that if there's an issue with SQL or fragment expansion, at least something shows up in the log, even if debug is not on. --- .../hl7/fhir/igtools/publisher/Publisher.java | 2 + .../StructureDefinitionRenderer.java | 104 +++++++++++++++++- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java index 457d23eb..0a52d58f 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java @@ -11341,6 +11341,8 @@ private byte[] processCustomLiquid(DBBuilder db, byte[] content, FetchedFile f) } catch (Exception e) { if (debug) { e.printStackTrace(); + } else { + System.out.println("Error processing custom liquid: " + e.getMessage()); } return content; } diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java index 2d98ba92..161a4e4f 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -109,6 +110,16 @@ public BindingResolutionDetails(String vss, String vsn) { public static final String ANCHOR_PREFIX_MS = "ms_"; public static final String ANCHOR_PREFIX_KEY = "key_"; + private static List SIGNIFICANT_EXTENSIONS = new ArrayList(Arrays.asList(new String[] + {"http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestPractice", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-graphConstraint", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxDecimalPlaces", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxSize", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-mimeType", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-minLength", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-obligation"} + )); ProfileUtilities utils; private StructureDefinition sd; private String destDir; @@ -624,12 +635,14 @@ private void scanForMustSupport(Map mustSupport, List protected List getKeyElements() { if (keyElements==null) { + boolean keyEligible = sd.getDerivation().equals(TypeDerivationRule.CONSTRAINT) && !sd.getKind().equals(StructureDefinitionKind.LOGICAL); keyElements = new ArrayList(); Map mustSupport = getMustSupport(); Set keyElementsSet = new HashSet(); - scanForKeyElements(keyElementsSet, mustSupport, sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep(), null); + if (keyEligible) + scanForKeyElements(keyElementsSet, mustSupport, sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep(), null); for (ElementDefinition ed : sd.getSnapshot().getElement()) { - if (keyElementsSet.contains(ed)) { + if (!keyEligible || keyElementsSet.contains(ed)) { ElementDefinition edCopy = ed.copy(); edCopy.copyUserData(ed); keyElements.add(edCopy); @@ -653,12 +666,97 @@ private void scanForKeyElements(Set keyElements, Map1) || child.getIsModifier() || (child.hasSlicing() && !child.getPath().endsWith(".extension") && !child.getPath().endsWith(".modifierExtension")) || child.hasSliceName() || getDifferential().containsKey(child.getId()) || !child.getMax().equals(child.getBase().getMax())) { + // - the min cardinality has been constrained from the base min cardinality + // - it has a vocabulary binding or additional binding that is required, extensible, minimum, maximum, or current binding that differs from the base resource. + // - it has a fixed value, pattern value, minValue, maxValue, maxLength, mustHaveValue, valueAlternatives + // - it has any of the following extensions: allowedUnits, bestPractice, graphConstraint, maxDecimalPlaces, maxSize, mimeType, minLength, obligation, + + List urls = extensionUrls(child.getExtension()); + urls.retainAll(SIGNIFICANT_EXTENSIONS); + boolean bindingChanged = false; + String basePath = child.getBase().getPath(); + StructureDefinition baseType = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + basePath.substring(0, basePath.indexOf("."))); + ElementDefinition baseElement = null; + for (ElementDefinition e: baseType.getSnapshot().getElement()) { + if (e.getPath().equals(basePath)) { + baseElement = e; + break; + } + } + if (baseElement==null) { + baseElement = baseElement; + //This shouldn't have happened + } else { + if (!baseElement.hasBinding()) + bindingChanged = true; + else { + ElementDefinitionBindingComponent baseBinding = baseElement.getBinding(); + ElementDefinitionBindingComponent binding = child.getBinding(); + if (binding.hasValueSet() && (binding.getStrength().equals(BindingStrength.REQUIRED) || binding.getStrength().equals(BindingStrength.EXTENSIBLE))) { + if (!baseBinding.hasStrength() || !binding.getStrength().toCode().equals(baseBinding.getStrength().toCode())) + bindingChanged = true; + else if (!baseBinding.hasValueSet() || !baseBinding.getValueSet().equals(binding.getValueSet())) + bindingChanged = true; + } + String additionalBindings = getAdditional(binding.getAdditional()); + String baseAdditionalBindings = getAdditional(binding.getAdditional()); + if (!additionalBindings.equals(baseAdditionalBindings)) + bindingChanged = true; + } + } + if (child.hasBinding()) { + if (baseType.hasSnapshot()) { + for (ElementDefinition e : baseType.getSnapshot().getElement()) { + if (e.getPath().equals(basePath)) { + baseElement = e; + break; + } + } + } + } + boolean oldMS = mustSupport.containsKey(child.getId()) || + child.getMin()!=0 || + (child.hasCondition() && child.getCondition().size()>1) || + child.getIsModifier() || + (child.hasSlicing() && !child.getPath().endsWith(".extension") && !child.getPath().endsWith(".modifierExtension")) || + child.hasSliceName() || + getDifferential().containsKey(child.getId()) || + !child.getMax().equals(child.getBase().getMax()); + boolean newMS = child.getMin()!=child.getBase().getMin() || + child.hasFixed() || + child.hasPattern() || + child.hasMinValue() && (baseElement==null || !baseElement.hasMinValue() || !child.getMinValue().toString().equals(baseElement.getMinValue().toString())) || + child.hasMaxValue() && (baseElement==null || !baseElement.hasMaxValue() || !child.getMaxValue().toString().equals(baseElement.getMaxValue().toString())) || + child.hasMaxLength() && (baseElement==null || !baseElement.hasMaxLength() || child.getMaxLength()!=baseElement.getMaxLength()) || + child.hasMustHaveValue() || + child.hasValueAlternatives() || + !urls.isEmpty(); + if ( oldMS || newMS) { scanForKeyElements(keyElements ,mustSupport, elements, child, baseModelUrl); } } } + private String getAdditional(List additionalBindings) { + String bindings = ""; + for (ElementDefinition.ElementDefinitionBindingAdditionalComponent additional: additionalBindings) { + if (additional.getPurpose().equals(ElementDefinition.AdditionalBindingPurposeVS.REQUIRED) || + additional.getPurpose().equals(ElementDefinition.AdditionalBindingPurposeVS.EXTENSIBLE) || + additional.getPurpose().equals(ElementDefinition.AdditionalBindingPurposeVS.REQUIRED)) + bindings += additional.getPurpose().getDisplay()+":" + additional.getValueSet()+";"; + } + return bindings; + } + + private List extensionUrls(List extensions) { + List urls = new ArrayList(); + for (Extension e: extensions) { + if (!urls.contains(e.getUrl())) + urls.add(e.getUrl()); + } + return urls; + } + public String grid(String defnFile, Set outputTracker) throws IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException { if (sd.getSnapshot().getElement().isEmpty()) return "";