From c1262d50c57231cac6ce5250e091553f9479a518 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 30 Dec 2022 07:20:06 +1300 Subject: [PATCH 1/4] fix up cardinality issues --- .../PEDefinitionSubExtension.java | 2 +- .../hl7/fhir/r5/profilemodel/PEInstance.java | 4 + .../r5/profilemodel/gen/PEGeneratedBase.java | 19 ++++- .../r5/profilemodel/gen/ProfileExample.java | 85 +++++++++++++++++-- .../org/hl7/fhir/r5/profiles/PETests.java | 12 ++- 5 files changed, 109 insertions(+), 13 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java index 2e752e1c2..c195eccb3 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java @@ -65,7 +65,7 @@ public class PEDefinitionSubExtension extends PEDefinition { if (ved.isRequired() || eed.isProhibited()) { return "extension('"+ued.getFixed().primitiveValue()+"').value"; } else { - return "extension('"+ued.getFixed().primitiveValue()+"').extension"; + return "extension('"+ued.getFixed().primitiveValue()+"')"; } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEInstance.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEInstance.java index 31fe35180..184e49c53 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEInstance.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEInstance.java @@ -236,4 +236,8 @@ public class PEInstance { public String getPath() { return path; } + + public Base getBase() { + return data; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PEGeneratedBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PEGeneratedBase.java index dfd66b2a8..b36130735 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PEGeneratedBase.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PEGeneratedBase.java @@ -1,17 +1,32 @@ package org.hl7.fhir.r5.profilemodel.gen; +import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.profilemodel.PEInstance; public class PEGeneratedBase { protected PEInstance instance; - protected void removeChild(String string) { - PEInstance child = instance.child("simple"); + protected void removeChild(String name) { + PEInstance child = instance.child(name); if (child != null) { instance.removeChild(child); } } + + protected void removeChildren(String name) { + for (PEInstance child : instance.children(name)) { + instance.removeChild(child); + } + } + + public PEInstance getInstance() { + return instance; + } + + public Base getData() { + return instance.getBase(); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ProfileExample.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ProfileExample.java index 27a4446cc..a966abe1e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ProfileExample.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/ProfileExample.java @@ -1,5 +1,8 @@ package org.hl7.fhir.r5.profilemodel.gen; +import java.util.ArrayList; +import java.util.List; + import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.CodeableConcept; @@ -60,37 +63,103 @@ public class ProfileExample extends PEGeneratedBase { } } + + public static class ProfileExampleComplexSlice3 extends PEGeneratedBase { + private ProfileExampleComplexSlice3(PEInstance instance) { + super(); + this.instance = instance; + } + + public List getSlice3a() { + List res = new ArrayList<>(); + for (PEInstance pe : instance.children("slice3a")) { + res.add((Coding) pe.asDataType()); + } + return res; + } + + public boolean hasSlice3a() { + return instance.children("slice3a").size() > 0; + } + + public ProfileExampleComplexSlice3 clearSlice3a() { + removeChildren("slice3a"); + return this; + } + + + public List getSlice3b() { + List res = new ArrayList<>(); + for (PEInstance pe : instance.children("slice3b")) { + res.add((StringType) pe.asDataType()); + } + return res; + } + + public boolean hasSlice3b() { + return instance.children("slice3b").size() > 0; + } + + public ProfileExampleComplexSlice3 clearSlice3b() { + removeChildren("slice3b"); + return this; + } + } + public static class ProfileExampleComplex extends PEGeneratedBase { private ProfileExampleComplex(PEInstance instance) { super(); this.instance = instance; } - public Coding getSlice1() { - return (Coding) instance.forceChild("slice1").asDataType(); + public List getSlice1() { + List res = new ArrayList<>(); + for (PEInstance pe : instance.children("slice1")) { + res.add((Coding) pe.asDataType()); + } + return res; } public boolean hasSlice1() { - return instance.child("slice1") != null; + return instance.children("slice1").size() > 0; } public ProfileExampleComplex clearSlice1() { - removeChild("slice1x`"); + removeChildren("slice1"); return this; } - public StringType getSlice2() { - return (StringType) instance.forceChild("slice2").asDataType(); + public List getSlice2() { + List res = new ArrayList<>(); + for (PEInstance pe : instance.children("slice2")) { + res.add((StringType) pe.asDataType()); + } + return res; } public boolean hasSlice2() { - return instance.child("slice1") != null; + return instance.children("slice2").size() > 0; } public ProfileExampleComplex clearSlice2() { - removeChild("slice1"); + removeChildren("slice1"); return this; } + + public ProfileExampleComplexSlice3 getSlice3() { + PEInstance pe = instance.forceChild("slice3"); + return new ProfileExampleComplexSlice3(pe); + } + + public boolean hasComplex() { + return instance.child("slice3") != null; + } + + public ProfileExampleComplex clearComplex() { + removeChild("slice3"); + return this; + } + } public ProfileExample(IWorkerContext context, Observation observation) { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java index 39e24880b..093529dd6 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java @@ -16,6 +16,7 @@ import org.hl7.fhir.r5.profilemodel.PEType; import org.hl7.fhir.r5.profilemodel.gen.ProfileExample; import org.hl7.fhir.r5.profilemodel.gen.ProfileExample.LOINCCodesForCholesterolInSerumPlasma; import org.hl7.fhir.r5.profilemodel.gen.ProfileExample.ProfileExampleComplex; +import org.hl7.fhir.r5.profilemodel.gen.ProfileExample.ProfileExampleComplexSlice3; import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; import org.hl7.fhir.r5.profilemodel.PEInstance.PEInstanceDataKind; @@ -351,14 +352,21 @@ public class PETests { Assertions.assertEquals(PEInstanceDataKind.Primitive, slice2.getDataKind()); Assertions.assertEquals("A string value", slice2.getPrimitiveAsString()); + PEInstance slice3 = complex.child("slice3"); + Assertions.assertNotNull(slice3); + Assertions.assertEquals("TestProfile.complex.slice3", slice3.getPath()); + ProfileExample ex = new ProfileExample(obs); Assertions.assertEquals(ObservationStatus.FINAL, ex.getStatus()); Assertions.assertEquals("76690-7", ex.getCode().getCodingFirstRep().getCode()); Assertions.assertEquals(LOINCCodesForCholesterolInSerumPlasma.L14647_2, ex.getSimple()); ProfileExampleComplex cplx = ex.getComplex(); Assertions.assertNotNull(cplx); - Assertions.assertEquals("18767-4", cplx.getSlice1().getCode()); - Assertions.assertEquals("A string value", cplx.getSlice2().primitiveValue()); + Assertions.assertEquals("18767-4", cplx.getSlice1().get(0).getCode()); + Assertions.assertEquals("A string value", cplx.getSlice2().get(0).primitiveValue()); + ProfileExampleComplexSlice3 sl3 = cplx.getSlice3(); + Assertions.assertEquals("56874-1", sl3.getSlice3a().get(0).getCode()); + Assertions.assertEquals("Another string value", sl3.getSlice3b().get(0).primitiveValue()); } From b014401dd4629cb450e5e432f25ff3a1f7458fc0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 1 Jan 2023 11:12:44 +1100 Subject: [PATCH 2/4] refactor profile rendering (more to StructureDefinitionRenderer) --- .../fhir/r5/comparison/ComparisonSession.java | 2 +- .../fhir/r5/comparison/ProfileComparer.java | 22 +- .../AdditionalBindingsRenderer.java | 15 +- .../profile/BindingResolution.java | 13 + .../profile/PathSlicingParams.java | 3 +- .../profile/ProfileKnowledgeProvider.java | 19 + .../profile/ProfilePathProcessor.java | 60 +- .../profile/ProfilePathProcessorState.java | 3 +- .../conformance/profile/ProfileUtilities.java | 3031 +++-------------- .../hl7/fhir/r5/context/ContextUtilities.java | 3 +- .../hl7/fhir/r5/renderers/DataRenderer.java | 1 - .../org/hl7/fhir/r5/renderers/Renderer.java | 3 +- .../fhir/r5/renderers/RendererFactory.java | 1 - .../r5/renderers/RequirementsRenderer.java | 2 - .../StructureDefinitionRenderer.java | 2224 +++++++++++- .../r5/renderers/utils/RenderingContext.java | 5 +- .../structuremap/StructureMapUtilities.java | 2 +- .../org/hl7/fhir/r5/profiles/PETests.java | 2 +- .../r5/test/NarrativeGenerationTests.java | 3 +- .../fhir/r5/test/SnapShotGenerationTests.java | 3 +- .../tests/SnapShotGenerationXTests.java | 3 +- 21 files changed, 2732 insertions(+), 2688 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/BindingResolution.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileKnowledgeProvider.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java index 13969d088..75abf98d9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java @@ -13,8 +13,8 @@ import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; import org.hl7.fhir.r5.comparison.ProfileComparer.ProfileComparison; import org.hl7.fhir.r5.comparison.ResourceComparer.ResourceComparison; import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CapabilityStatement; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java index db4f81196..60dd0deab 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java @@ -11,7 +11,6 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.UnusedTracker; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.JsonParser; @@ -33,10 +32,16 @@ import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer; +import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.utils.DefinitionNavigator; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; @@ -988,22 +993,27 @@ public class ProfileComparer extends CanonicalResourceComparer { String sn = getSliceName(combined); if (sn != null) sName = sName +":"+sn; - UnusedTracker used = new UnusedTracker(); + StructureDefinitionRenderer.UnusedTracker used = new StructureDefinitionRenderer.UnusedTracker(); + StructureDefinitionRenderer sdrLeft = new StructureDefinitionRenderer(new RenderingContext(utilsLeft.getContext(), null, utilsLeft.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + StructureDefinitionRenderer sdrRight= new StructureDefinitionRenderer(new RenderingContext(utilsRight.getContext(), null, utilsRight.getTerminologyServiceOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + + + Cell nc; 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 = utilsLeft.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName, null); + nc = sdrLeft.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName, null); } else { - nc = utilsRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName, null); + nc = sdrRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName, null); } if (combined.hasLeft()) { - frame(utilsLeft.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, null), leftColor); + frame(sdrLeft.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, null), leftColor); } else { frame(spacers(row, 4, gen), leftColor); } if (combined.hasRight()) { - 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, null), rightColor); + frame(sdrRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false, null), rightColor); } else { frame(spacers(row, 4, gen), rightColor); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java index 035c4188f..9f0da47e9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java @@ -7,9 +7,9 @@ import java.util.List; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.UsageContext; @@ -106,7 +106,7 @@ public class AdditionalBindingsRenderer { abr.compare = new AdditionalBindingDetail(); abr.compare.valueSet = compExt==null ? null : compExt.getValue().primitiveValue(); } else { - abr.isUnchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS); + abr.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS); } bindings.add(abr); } @@ -166,7 +166,7 @@ public class AdditionalBindingsRenderer { abr.docoShort = ext.getExtensionString("shortDoco"); abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null; abr.any = "any".equals(ext.getExtensionString("scope")); - abr.isUnchanged = ext.hasUserData(ProfileUtilities.DERIVATION_EQUALS); + abr.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS); return abr; } @@ -309,16 +309,19 @@ public class AdditionalBindingsRenderer { case "minimum": td.ah(corePath+"extension-elementdefinition-minvalueset.html", "The minimum allowable value set - any conformant system SHALL support all these codes").tx("Min Binding"); break; - case "conformance" : + case "required" : td.ah(corePath+"terminologies.html#strength", "Validators will check this binding (strength = required)").tx("Validation Binding"); break; + case "extensible" : + td.ah(corePath+"terminologies.html#strength", "Validators will check this binding (strength = extensible)").tx("Validation Binding"); + break; case "candidate" : td.ah(corePath+"terminologies.html#strength", "This is a candidate binding that constraints on this profile may consider (see doco)").tx("Candidate Validation Binding"); break; case "current" : td.span(null, "New records are required to use this value set, but legacy records may use other codes").tx("Required"); break; - case "recommended" : + case "preferred" : td.span(null, "This is the value set that is recommended (documentation should explain why)").tx("Recommended"); break; case "ui" : diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/BindingResolution.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/BindingResolution.java new file mode 100644 index 000000000..648498505 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/BindingResolution.java @@ -0,0 +1,13 @@ +package org.hl7.fhir.r5.conformance.profile; + +public class BindingResolution { + public BindingResolution(String display, String url) { + this.display = display; + this.url = url; + } + public BindingResolution() { + // TODO Auto-generated constructor stub + } + public String display; + public String url; +} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/PathSlicingParams.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/PathSlicingParams.java index 7563e18c8..c04986364 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/PathSlicingParams.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/PathSlicingParams.java @@ -1,9 +1,10 @@ package org.hl7.fhir.r5.conformance.profile; +import org.hl7.fhir.r5.model.ElementDefinition; + import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import org.hl7.fhir.r5.model.ElementDefinition; @AllArgsConstructor(access = AccessLevel.PROTECTED) public class PathSlicingParams { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileKnowledgeProvider.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileKnowledgeProvider.java new file mode 100644 index 000000000..c8f59bcd6 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileKnowledgeProvider.java @@ -0,0 +1,19 @@ +package org.hl7.fhir.r5.conformance.profile; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.StructureDefinition; + +public interface ProfileKnowledgeProvider { + boolean isDatatype(String typeSimple); + boolean isPrimitiveType(String typeSimple); + boolean isResource(String typeSimple); + boolean hasLinkFor(String typeSimple); + String getLinkFor(String corePath, String typeSimple); + BindingResolution resolveBinding(StructureDefinition def, + ElementDefinitionBindingComponent binding, String path) throws FHIRException; + BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException; + String getLinkForProfile(StructureDefinition profile, String url); + boolean prependLinks(); + String getLinkForUrl(String corePath, String s); +} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java index 60559e0ee..ad0157de4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java @@ -1,24 +1,26 @@ package org.hl7.fhir.r5.conformance.profile; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.With; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.ElementRedirection; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComponent; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.With; @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ProfilePathProcessor { @@ -279,7 +281,7 @@ public class ProfilePathProcessor { throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.NOT_DONE_YET)); } if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), false)) { - if (profileUtilities.baseHasChildren(cursors.base, currentBase)) { // not a new type here + if (baseHasChildren(cursors.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 (@ " + currentBasePath + " | " + currentBase.getPath() + ")"); } else { StructureDefinition dt = profileUtilities.getTypeForElement(getDifferential(), cursors.diffCursor, getProfileName(), diffMatches, outcome, getWebUrl(), getDerived()); @@ -689,6 +691,28 @@ public class ProfilePathProcessor { return res; } + private int indexOfFirstNonChild(StructureDefinitionSnapshotComponent base, ElementDefinition currentBase, int i, int baseLimit) { + return baseLimit+1; + } + + private boolean baseHasChildren(StructureDefinitionSnapshotComponent base, ElementDefinition ed) { + int index = base.getElement().indexOf(ed); + if (index == -1 || index >= base.getElement().size()-1) + return false; + String p = base.getElement().get(index+1).getPath(); + return isChildOf(p, ed.getPath()); + } + + + private boolean isChildOf(String sub, String focus) { + if (focus.endsWith("[x]")) { + focus = focus.substring(0, focus.length()-3); + return sub.startsWith(focus); + } else + return sub.startsWith(focus+"."); + } + + private void processSimplePathWithEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List diffMatches, ProfilePathProcessorState cursors) { ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy()); outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); @@ -703,10 +727,10 @@ public class ProfilePathProcessor { if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) { // well, the profile walks into this, so we need to as well // did we implicitly step into a new type? - if (profileUtilities.baseHasChildren(cursors.base, currentBase)) { // not a new type here + if (baseHasChildren(cursors.base, currentBase)) { // not a new type here this.incrementDebugIndent().withSlicing(new PathSlicingParams()). processPaths( new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase)); - cursors.baseCursor = profileUtilities.indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor + 1, getBaseLimit()); + cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor + 1, getBaseLimit()); } else { if (outcome.getType().size() == 0 && !outcome.hasContentReference()) { @@ -858,7 +882,7 @@ public class ProfilePathProcessor { profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), closed, getUrl(), getSourceStructureDefinition(), getDerived()); // if there's no slice, we don't want to update the unsliced description profileUtilities.removeStatusExtensions(outcome); } else if (!diffMatches.get(0).hasSliceName()) { - diffMatches.get(0).setUserData(profileUtilities.GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called + diffMatches.get(0).setUserData(profileUtilities.UD_GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called } getResult().getElement().add(outcome); @@ -954,8 +978,8 @@ public class ProfilePathProcessor { outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); if (!outcome.getPath().startsWith(cursors.resultPathBase)) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); - outcome.setUserData(profileUtilities.BASE_PATH, outcome.getPath()); - outcome.setUserData(profileUtilities.BASE_MODEL, getSourceStructureDefinition().getUrl()); + outcome.setUserData(profileUtilities.UD_BASE_PATH, outcome.getPath()); + outcome.setUserData(profileUtilities.UD_BASE_MODEL, getSourceStructureDefinition().getUrl()); getResult().getElement().add(outcome); cursors.baseCursor++; } @@ -1223,13 +1247,13 @@ public class ProfilePathProcessor { getResult().getElement().add(outcome); // the profile walks into this, so we need to as well // did we implicitly step into a new type? - if (profileUtilities.baseHasChildren(cursors.base, currentBase)) { // not a new type here + if (baseHasChildren(cursors.base, currentBase)) { // not a new type here this .incrementDebugIndent() .withSlicing(new PathSlicingParams()).processPaths( new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase)); - cursors.baseCursor = profileUtilities.indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor, getBaseLimit()); + cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor, getBaseLimit()); } else { StructureDefinition dt = profileUtilities.getTypeForElement(getDifferential(), cursors.diffCursor, getProfileName(), diffMatches, outcome, getWebUrl(), getDerived()); cursors.contextName = dt.getUrl(); @@ -1264,8 +1288,8 @@ public class ProfilePathProcessor { if (!outcome.getPath().startsWith(cursors.resultPathBase)) throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, getProfileName(), outcome.getPath(), cursors.resultPathBase)); getResult().getElement().add(outcome); // so we just copy it in - outcome.setUserData(profileUtilities.BASE_MODEL, getSourceStructureDefinition().getUrl()); - outcome.setUserData(profileUtilities.BASE_PATH, cursors.resultPathBase); + outcome.setUserData(profileUtilities.UD_BASE_MODEL, getSourceStructureDefinition().getUrl()); + outcome.setUserData(profileUtilities.UD_BASE_PATH, cursors.resultPathBase); cursors.baseCursor++; } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessorState.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessorState.java index fb3cd43d6..b6bec6b2e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessorState.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessorState.java @@ -1,8 +1,9 @@ package org.hl7.fhir.r5.conformance.profile; +import org.hl7.fhir.r5.model.StructureDefinition; + import lombok.AccessLevel; import lombok.AllArgsConstructor; -import org.hl7.fhir.r5.model.StructureDefinition; @AllArgsConstructor(access = AccessLevel.PROTECTED) public class ProfilePathProcessorState { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 6b19cde28..01bb8b89d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -44,29 +44,18 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.conformance.AdditionalBindingsRenderer; import org.hl7.fhir.r5.conformance.ElementRedirection; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution; import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.ObjectConverter; import org.hl7.fhir.r5.elementmodel.Property; -import org.hl7.fhir.r5.formats.IParser; -import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.BooleanType; -import org.hl7.fhir.r5.model.CanonicalType; -import org.hl7.fhir.r5.model.CodeType; -import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DataType; -import org.hl7.fhir.r5.model.Element; import org.hl7.fhir.r5.model.ElementDefinition; -import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBaseComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; @@ -75,10 +64,8 @@ import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionExampleComponent import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; -import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; -import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumerations.BindingStrength; import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; @@ -87,14 +74,10 @@ import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.IntegerType; -import org.hl7.fhir.r5.model.PrimitiveType; -import org.hl7.fhir.r5.model.Quantity; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.ExtensionContextType; -import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionContextComponent; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionDifferentialComponent; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; @@ -104,21 +87,14 @@ import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r5.renderers.TerminologyRenderer; -import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.PublicationHacker; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.TranslatingUtilities; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; import org.hl7.fhir.r5.utils.formats.CSVWriter; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.MarkDownProcessor; -import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; @@ -127,13 +103,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationOptions; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; -import org.hl7.fhir.utilities.xhtml.NodeType; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; -import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xml.SchematronWriter; import org.hl7.fhir.utilities.xml.SchematronWriter.Rule; import org.hl7.fhir.utilities.xml.SchematronWriter.SchematronType; @@ -225,11 +195,18 @@ public class ProfileUtilities extends TranslatingUtilities { public String getName() { return name; } + public boolean isMandatory() { + return mandatory; + } + public void setMandatory(boolean mandatory) { + this.mandatory = mandatory; + } + } private static final int MAX_RECURSION_LIMIT = 10; - public class ExtensionContext { + public static class ExtensionContext { private ElementDefinition element; private StructureDefinition defn; @@ -268,39 +245,23 @@ public class ProfileUtilities extends TranslatingUtilities { } } - public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; - private static final String ROW_COLOR_ERROR = "#ffcccc"; - private static final String ROW_COLOR_FATAL = "#ff9999"; - private static final String ROW_COLOR_WARNING = "#ffebcc"; - private static final String ROW_COLOR_HINT = "#ebf5ff"; - private static final String ROW_COLOR_NOT_MUST_SUPPORT = "#d6eaf8"; - public static final int STATUS_OK = 0; - public static final int STATUS_HINT = 1; - public static final int STATUS_WARNING = 2; - public static final int STATUS_ERROR = 3; - public static final int STATUS_FATAL = 4; - - public static final String BASE_MODEL = "base.model"; - public static final String BASE_PATH = "base.path"; - public static final String DERIVATION_EQUALS = "derivation.equals"; - public static final String DERIVATION_POINTER = "derived.pointer"; - public static final String IS_DERIVED = "derived.fact"; - public static final String UD_ERROR_STATUS = "error-status"; - protected static final String GENERATED_IN_SNAPSHOT = "profileutilities.snapshot.processed"; + public static final String UD_BASE_MODEL = "base.model"; + public static final String UD_BASE_PATH = "base.path"; + public static final String UD_DERIVATION_EQUALS = "derivation.equals"; + public static final String UD_DERIVATION_POINTER = "derived.pointer"; + public static final String UD_IS_DERIVED = "derived.fact"; + public static final String UD_GENERATED_IN_SNAPSHOT = "profileutilities.snapshot.processed"; private static final boolean COPY_BINDING_EXTENSIONS = false; private static final boolean DONT_DO_THIS = false; - private final boolean ADD_REFERENCE_TO_TABLE = true; - - private boolean useTableForFixedValues = true; + private boolean debug; - // note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here private final IWorkerContext context; private FHIRPathEngine fpe; private List messages; private List snapshotStack = new ArrayList(); private ProfileKnowledgeProvider pkp; - private boolean igmode; +// private boolean igmode; private boolean exception; private ValidationOptions terminologyServiceOptions = new ValidationOptions(); private boolean newSlicingProcessing; @@ -310,7 +271,6 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean wantFixDifferentialFirstElementType; private Set masterSourceFileNames; private Map childMapCache = new HashMap<>(); - private List keyRows = new ArrayList<>(); public ProfileUtilities(IWorkerContext context, List messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) { super(); @@ -333,18 +293,6 @@ public class ProfileUtilities extends TranslatingUtilities { this.fpe = new FHIRPathEngine(context, this); } } - - public static class UnusedTracker { - private boolean used; - } - - public boolean isIgmode() { - return igmode; - } - - public void setIgmode(boolean igmode) { - this.igmode = igmode; - } public boolean isWantFixDifferentialFirstElementType() { return wantFixDifferentialFirstElementType; @@ -363,31 +311,6 @@ public class ProfileUtilities extends TranslatingUtilities { return this; } - public interface ProfileKnowledgeProvider { - class BindingResolution { - public BindingResolution(String display, String url) { - this.display = display; - this.url = url; - } - public BindingResolution() { - // TODO Auto-generated constructor stub - } - public String display; - public String url; - } - boolean isDatatype(String typeSimple); - boolean isPrimitiveType(String typeSimple); - boolean isResource(String typeSimple); - boolean hasLinkFor(String typeSimple); - String getLinkFor(String corePath, String typeSimple); - BindingResolution resolveBinding(StructureDefinition def, - ElementDefinitionBindingComponent binding, String path) throws FHIRException; - BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException; - String getLinkForProfile(StructureDefinition profile, String url); - boolean prependLinks(); - String getLinkForUrl(String corePath, String s); - } - public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { if (childMapCache.containsKey(element)) { return childMapCache.get(element); @@ -550,7 +473,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } - public void updateMaps(StructureDefinition base, StructureDefinition derived) throws DefinitionException { + private void updateMaps(StructureDefinition base, StructureDefinition derived) throws DefinitionException { if (base == null) throw new DefinitionException(context.formatMessage(I18nConstants.NO_BASE_PROFILE_PROVIDED)); if (derived == null) @@ -636,7 +559,7 @@ public class ProfileUtilities extends TranslatingUtilities { for (ElementDefinition e : derived.getDifferential().getElement()) - e.clearUserData(GENERATED_IN_SNAPSHOT); + e.clearUserData(UD_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 @@ -656,9 +579,9 @@ public class ProfileUtilities extends TranslatingUtilities { checkGroupConstraints(derived); if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition e : diff.getElement()) { - if (!e.hasUserData(GENERATED_IN_SNAPSHOT) && e.getPath().contains(".")) { + if (!e.hasUserData(UD_GENERATED_IN_SNAPSHOT) && e.getPath().contains(".")) { ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); - e.setUserData(GENERATED_IN_SNAPSHOT, outcome); + e.setUserData(UD_GENERATED_IN_SNAPSHOT, outcome); derived.getSnapshot().addElement(outcome); if (walksInto(diff.getElement(), e)) { if (e.getType().size() > 1) { @@ -691,12 +614,12 @@ public class ProfileUtilities extends TranslatingUtilities { 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(UD_DERIVATION_EQUALS)) + ((Base) e.getUserData("diff-source")).setUserData(UD_DERIVATION_EQUALS, e.getUserData(UD_DERIVATION_EQUALS)); + if (e.hasUserData(UD_DERIVATION_POINTER)) + ((Base) e.getUserData("diff-source")).setUserData(UD_DERIVATION_POINTER, e.getUserData(UD_DERIVATION_POINTER)); } - if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { + if (!e.hasUserData(UD_GENERATED_IN_SNAPSHOT)) { b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath()); ce++; if (e.hasId()) { @@ -860,7 +783,7 @@ public class ProfileUtilities extends TranslatingUtilities { * @param derived * @throws Error */ - public void checkDifferentialBaseType(StructureDefinition derived) throws Error { + private void checkDifferentialBaseType(StructureDefinition derived) throws Error { if (derived.hasDifferential() && !derived.getDifferential().getElementFirstRep().getPath().contains(".") && !derived.getDifferential().getElementFirstRep().getType().isEmpty()) { if (wantFixDifferentialFirstElementType && typeMatchesAncestor(derived.getDifferential().getElementFirstRep().getType(), derived.getBaseDefinition(), derived)) { derived.getDifferential().getElementFirstRep().getType().clear(); @@ -887,7 +810,7 @@ public class ProfileUtilities extends TranslatingUtilities { derived.getSnapshot().getElement().removeAll(toRemove); } - public void checkForChildrenInGroup(StructureDefinition derived, List toRemove, ElementDefinition element) throws Error { + private void checkForChildrenInGroup(StructureDefinition derived, List toRemove, ElementDefinition element) throws Error { List children = getChildren(derived, element); List groups = readChoices(element, children); for (ElementChoiceGroup group : groups) { @@ -1076,18 +999,18 @@ public class ProfileUtilities extends TranslatingUtilities { } - private String typeSummary(ElementDefinition ed) { - StringBuilder b = new StringBuilder(); - boolean first = true; - for (TypeRefComponent tr : ed.getType()) { - if (first) - first = false; - else - b.append("|"); - b.append(tr.getWorkingCode()); - } - return b.toString(); - } +// private String typeSummary(ElementDefinition ed) { +// StringBuilder b = new StringBuilder(); +// boolean first = true; +// for (TypeRefComponent tr : ed.getType()) { +// if (first) +// first = false; +// else +// b.append("|"); +// b.append(tr.getWorkingCode()); +// } +// return b.toString(); +// } private String typeSummaryWithProfile(ElementDefinition ed) { StringBuilder b = new StringBuilder(); @@ -1109,17 +1032,17 @@ public class ProfileUtilities extends TranslatingUtilities { } - private boolean findMatchingElement(String id, List list) { - for (ElementDefinition ed : list) { - if (ed.getId().equals(id)) - return true; - if (id.endsWith("[x]")) { - if (ed.getId().startsWith(id.substring(0, id.length()-3)) && !ed.getId().substring(id.length()-3).contains(".")) - return true; - } - } - return false; - } +// private boolean findMatchingElement(String id, List list) { +// for (ElementDefinition ed : list) { +// if (ed.getId().equals(id)) +// return true; +// if (id.endsWith("[x]")) { +// if (ed.getId().startsWith(id.substring(0, id.length()-3)) && !ed.getId().substring(id.length()-3).contains(".")) +// return true; +// } +// } +// return false; +// } protected ElementDefinition getById(List list, String baseId) { for (ElementDefinition t : list) { @@ -1147,7 +1070,7 @@ public class ProfileUtilities extends TranslatingUtilities { return result; } - public StructureDefinition getTypeForElement(StructureDefinitionDifferentialComponent differential, int diffCursor, String profileName, + StructureDefinition getTypeForElement(StructureDefinitionDifferentialComponent differential, int diffCursor, String profileName, List diffMatches, ElementDefinition outcome, String webUrl, Resource srcSD) { if (outcome.getType().size() == 0) { if (outcome.hasContentReference()) { @@ -1251,7 +1174,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public String determineFixedType(List diffMatches, String fixedType, int i) { + String determineFixedType(List diffMatches, String fixedType, int i) { if (diffMatches.get(i).getType().size() == 0 && diffMatches.get(i).hasSliceName()) { String n = tail(diffMatches.get(i).getPath()).replace("[x]", ""); String t = diffMatches.get(i).getSliceName().substring(n.length()); @@ -1330,29 +1253,8 @@ public class ProfileUtilities extends TranslatingUtilities { return index >=0 && index < list.size() ? list.get(index).present() : "X"; } - public boolean baseHasChildren(StructureDefinitionSnapshotComponent base, ElementDefinition ed) { - int index = base.getElement().indexOf(ed); - if (index == -1 || index >= base.getElement().size()-1) - return false; - String p = base.getElement().get(index+1).getPath(); - return isChildOf(p, ed.getPath()); - } - - - private boolean isChildOf(String sub, String focus) { - if (focus.endsWith("[x]")) { - focus = focus.substring(0, focus.length()-3); - return sub.startsWith(focus); - } else - return sub.startsWith(focus+"."); - } - - - public int indexOfFirstNonChild(StructureDefinitionSnapshotComponent base, ElementDefinition currentBase, int i, int baseLimit) { - return baseLimit+1; - } - - + + protected String rootName(String cpath) { String t = tail(cpath); return t.replace("[x]", ""); @@ -1539,11 +1441,11 @@ public class ProfileUtilities extends TranslatingUtilities { protected void markDerived(ElementDefinition outcome) { for (ElementDefinitionConstraintComponent inv : outcome.getConstraint()) - inv.setUserData(IS_DERIVED, true); + inv.setUserData(UD_IS_DERIVED, true); } - public static String summarizeSlicing(ElementDefinitionSlicingComponent slice) { + static String summarizeSlicing(ElementDefinitionSlicingComponent slice) { StringBuilder b = new StringBuilder(); boolean first = true; for (ElementDefinitionSlicingDiscriminatorComponent d : slice.getDiscriminator()) { @@ -1570,8 +1472,8 @@ public class ProfileUtilities extends TranslatingUtilities { protected void updateFromBase(ElementDefinition derived, ElementDefinition base, String baseProfileUrl) { - derived.setUserData(BASE_MODEL, baseProfileUrl); - derived.setUserData(BASE_PATH, base.getPath()); + derived.setUserData(UD_BASE_MODEL, baseProfileUrl); + derived.setUserData(UD_BASE_PATH, base.getPath()); if (base.hasBase()) { if (!derived.hasBase()) derived.setBase(new ElementDefinitionBaseComponent()); @@ -1669,8 +1571,7 @@ public class ProfileUtilities extends TranslatingUtilities { return sd; } - - public static String typeCode(List types) { + static String typeCode(List types) { StringBuilder b = new StringBuilder(); boolean first = true; for (TypeRefComponent type : types) { @@ -1703,7 +1604,7 @@ public class ProfileUtilities extends TranslatingUtilities { * @param element - the Element to update * @return - the updated Element */ - public ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element) { + ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element) { if (element != null) { ElementDefinition defn = element; if (defn.hasBinding() && defn.getBinding().hasValueSet() && defn.getBinding().getValueSet().startsWith("#")) @@ -1733,7 +1634,7 @@ public class ProfileUtilities extends TranslatingUtilities { return element; } - public static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set baseFilenames, Set localFilenames, boolean processRelatives) { + private static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set baseFilenames, Set localFilenames, boolean processRelatives) { if (markdown == null) { return ""; } @@ -1786,8 +1687,7 @@ public class ProfileUtilities extends TranslatingUtilities { return b.toString(); } - - public static boolean issLocalFileName(String url, Set localFilenames) { + private static boolean issLocalFileName(String url, Set localFilenames) { if (localFilenames != null) { for (String n : localFilenames) { if (url.startsWith(n.toLowerCase())) { @@ -1799,7 +1699,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public static boolean isLikelySourceURLReference(String url, List resourceNames, Set baseFilenames, Set localFilenames) { + private static boolean isLikelySourceURLReference(String url, List resourceNames, Set baseFilenames, Set localFilenames) { if (resourceNames != null) { for (String n : resourceNames) { if (url.startsWith(n.toLowerCase()+".html")) { @@ -1920,7 +1820,7 @@ public class ProfileUtilities extends TranslatingUtilities { return currentBase.getPath().endsWith(".extension") || currentBase.getPath().endsWith(".modifierExtension"); } - public boolean hasInnerDiffMatches(StructureDefinitionDifferentialComponent context, String path, int start, int end, List base, boolean allowSlices) throws DefinitionException { + boolean hasInnerDiffMatches(StructureDefinitionDifferentialComponent context, String path, int start, int end, List base, boolean allowSlices) throws DefinitionException { end = Math.min(context.getElement().size(), end); start = Math.max(0, start); @@ -1977,7 +1877,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public boolean isSameBase(String p, String sp) { + private boolean isSameBase(String p, String sp) { return (p.endsWith("[x]") && sp.startsWith(p.substring(0, p.length()-3))) || (sp.endsWith("[x]") && p.startsWith(sp.substring(0, sp.length()-3))) ; } @@ -2011,12 +1911,12 @@ public class ProfileUtilities extends TranslatingUtilities { } protected void updateFromDefinition(ElementDefinition dest, ElementDefinition source, String pn, boolean trimDifferential, String purl, StructureDefinition srcSD, StructureDefinition derivedSrc) throws DefinitionException, FHIRException { - source.setUserData(GENERATED_IN_SNAPSHOT, dest); + source.setUserData(UD_GENERATED_IN_SNAPSHOT, dest); // we start with a clone of the base profile ('dest') and we copy from the profile ('source') // over the top for anything the source has ElementDefinition base = dest; ElementDefinition derived = source; - derived.setUserData(DERIVATION_POINTER, base); + derived.setUserData(UD_DERIVATION_POINTER, base); boolean isExtension = checkExtensionDoco(base); @@ -2059,7 +1959,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setShortElement(null); else if (derived.hasShortElement()) - derived.getShortElement().setUserData(DERIVATION_EQUALS, true); + derived.getShortElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasDefinitionElement()) { @@ -2070,7 +1970,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setDefinitionElement(null); else if (derived.hasDefinitionElement()) - derived.getDefinitionElement().setUserData(DERIVATION_EQUALS, true); + derived.getDefinitionElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasCommentElement()) { @@ -2081,7 +1981,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) base.setCommentElement(derived.getCommentElement().copy()); else if (derived.hasCommentElement()) - derived.getCommentElement().setUserData(DERIVATION_EQUALS, true); + derived.getCommentElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasLabelElement()) { @@ -2092,7 +1992,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) base.setLabelElement(derived.getLabelElement().copy()); else if (derived.hasLabelElement()) - derived.getLabelElement().setUserData(DERIVATION_EQUALS, true); + derived.getLabelElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasRequirementsElement()) { @@ -2103,7 +2003,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) base.setRequirementsElement(derived.getRequirementsElement().copy()); else if (derived.hasRequirementsElement()) - derived.getRequirementsElement().setUserData(DERIVATION_EQUALS, true); + derived.getRequirementsElement().setUserData(UD_DERIVATION_EQUALS, true); } // sdf-9 if (derived.hasRequirements() && !base.getPath().contains(".")) @@ -2121,7 +2021,7 @@ public class ProfileUtilities extends TranslatingUtilities { derived.getAlias().clear(); else for (StringType t : derived.getAlias()) - t.setUserData(DERIVATION_EQUALS, true); + t.setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMinElement()) { @@ -2132,7 +2032,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setMinElement(null); else - derived.getMinElement().setUserData(DERIVATION_EQUALS, true); + derived.getMinElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMaxElement()) { @@ -2143,7 +2043,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setMaxElement(null); else - derived.getMaxElement().setUserData(DERIVATION_EQUALS, true); + derived.getMaxElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasFixed()) { @@ -2152,7 +2052,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setFixed(null); else - derived.getFixed().setUserData(DERIVATION_EQUALS, true); + derived.getFixed().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasPattern()) { @@ -2162,7 +2062,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (trimDifferential) derived.setPattern(null); else - derived.getPattern().setUserData(DERIVATION_EQUALS, true); + derived.getPattern().setUserData(UD_DERIVATION_EQUALS, true); } for (ElementDefinitionExampleComponent ex : derived.getExample()) { @@ -2175,7 +2075,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.getExample().remove(ex); else - ex.setUserData(DERIVATION_EQUALS, true); + ex.setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMaxLengthElement()) { @@ -2184,7 +2084,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setMaxLengthElement(null); else - derived.getMaxLengthElement().setUserData(DERIVATION_EQUALS, true); + derived.getMaxLengthElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMaxValue()) { @@ -2193,7 +2093,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setMaxValue(null); else - derived.getMaxValue().setUserData(DERIVATION_EQUALS, true); + derived.getMaxValue().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMinValue()) { @@ -2202,7 +2102,7 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setMinValue(null); else - derived.getMinValue().setUserData(DERIVATION_EQUALS, true); + derived.getMinValue().setUserData(UD_DERIVATION_EQUALS, true); } // todo: what to do about conditions? @@ -2217,7 +2117,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setMustSupportElement(null); else - derived.getMustSupportElement().setUserData(DERIVATION_EQUALS, true); + derived.getMustSupportElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMustHaveValueElement()) { @@ -2229,7 +2129,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setMustHaveValueElement(null); else - derived.getMustHaveValueElement().setUserData(DERIVATION_EQUALS, true); + derived.getMustHaveValueElement().setUserData(UD_DERIVATION_EQUALS, true); } @@ -2241,13 +2141,13 @@ public class ProfileUtilities extends TranslatingUtilities { else if (trimDifferential) derived.setIsModifierElement(null); else if (derived.hasIsModifierElement()) - derived.getIsModifierElement().setUserData(DERIVATION_EQUALS, true); + derived.getIsModifierElement().setUserData(UD_DERIVATION_EQUALS, true); if (derived.hasIsModifierReasonElement() && !(base.hasIsModifierReasonElement() && Base.compareDeep(derived.getIsModifierReasonElement(), base.getIsModifierReasonElement(), false))) base.setIsModifierReasonElement(derived.getIsModifierReasonElement().copy()); else if (trimDifferential) derived.setIsModifierReasonElement(null); else if (derived.hasIsModifierReasonElement()) - derived.getIsModifierReasonElement().setUserData(DERIVATION_EQUALS, true); + derived.getIsModifierReasonElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasBinding()) { @@ -2295,7 +2195,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setBinding(null); else - derived.getBinding().setUserData(DERIVATION_EQUALS, true); + derived.getBinding().setUserData(UD_DERIVATION_EQUALS, true); } // else if (base.hasBinding() && doesn't have bindable type ) // base @@ -2307,7 +2207,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (trimDifferential) derived.setIsSummaryElement(null); else - derived.getIsSummaryElement().setUserData(DERIVATION_EQUALS, true); + derived.getIsSummaryElement().setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasType()) { @@ -2328,7 +2228,7 @@ public class ProfileUtilities extends TranslatingUtilities { derived.getType().clear(); else for (TypeRefComponent t : derived.getType()) - t.setUserData(DERIVATION_EQUALS, true); + t.setUserData(UD_DERIVATION_EQUALS, true); } if (derived.hasMapping()) { @@ -2348,7 +2248,7 @@ public class ProfileUtilities extends TranslatingUtilities { derived.getMapping().clear(); } else { for (ElementDefinitionMappingComponent t : derived.getMapping()) { - t.setUserData(DERIVATION_EQUALS, true); + t.setUserData(UD_DERIVATION_EQUALS, true); } } } @@ -2360,7 +2260,7 @@ public class ProfileUtilities extends TranslatingUtilities { // todo: constraints are cumulative. there is no replacing for (ElementDefinitionConstraintComponent s : base.getConstraint()) { - s.setUserData(IS_DERIVED, true); + s.setUserData(UD_IS_DERIVED, true); if (!s.hasSource()) { s.setSource(srcSD.getUrl()); } @@ -2401,7 +2301,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } - public void checkTypeDerivation(String purl, StructureDefinition srcSD, ElementDefinition base, ElementDefinition derived, TypeRefComponent ts) { + private void checkTypeDerivation(String purl, StructureDefinition srcSD, ElementDefinition base, ElementDefinition derived, TypeRefComponent ts) { boolean ok = false; CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); String t = ts.getWorkingCode(); @@ -2462,7 +2362,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public void checkTypeOk(ElementDefinition dest, String ft, StructureDefinition sd, String fieldName) { + private void checkTypeOk(ElementDefinition dest, String ft, StructureDefinition sd, String fieldName) { boolean ok = false; Set types = new HashSet<>(); if (dest.getPath().contains(".")) { @@ -2536,199 +2436,200 @@ public class ProfileUtilities extends TranslatingUtilities { } return false; } + +// there was some proposal that this be surfaced in the IG publisher, but it is not currently available +// private void closeDifferential(StructureDefinition base, StructureDefinition derived) throws FHIRException { +// for (ElementDefinition edb : base.getSnapshot().getElement()) { +// if (isImmediateChild(edb) && !edb.getPath().endsWith(".id")) { +// ElementDefinition edm = getMatchInDerived(edb, derived.getDifferential().getElement()); +// if (edm == null) { +// ElementDefinition edd = derived.getDifferential().addElement(); +// edd.setPath(edb.getPath()); +// edd.setMax("0"); +// } else if (edb.hasSlicing()) { +// closeChildren(base, edb, derived, edm); +// } +// } +// } +// sortDifferential(base, derived, derived.getName(), new ArrayList(), false); +// } - public void closeDifferential(StructureDefinition base, StructureDefinition derived) throws FHIRException { - for (ElementDefinition edb : base.getSnapshot().getElement()) { - if (isImmediateChild(edb) && !edb.getPath().endsWith(".id")) { - ElementDefinition edm = getMatchInDerived(edb, derived.getDifferential().getElement()); - if (edm == null) { - ElementDefinition edd = derived.getDifferential().addElement(); - edd.setPath(edb.getPath()); - edd.setMax("0"); - } else if (edb.hasSlicing()) { - closeChildren(base, edb, derived, edm); - } - } - } - sortDifferential(base, derived, derived.getName(), new ArrayList(), false); - } +// private void closeChildren(StructureDefinition base, ElementDefinition edb, StructureDefinition derived, ElementDefinition edm) { +//// String path = edb.getPath()+"."; +// int baseStart = base.getSnapshot().getElement().indexOf(edb); +// int baseEnd = findEnd(base.getSnapshot().getElement(), edb, baseStart+1); +// int diffStart = derived.getDifferential().getElement().indexOf(edm); +// int diffEnd = findEnd(derived.getDifferential().getElement(), edm, diffStart+1); +// +// for (int cBase = baseStart; cBase < baseEnd; cBase++) { +// ElementDefinition edBase = base.getSnapshot().getElement().get(cBase); +// if (isImmediateChild(edBase, edb)) { +// ElementDefinition edMatch = getMatchInDerived(edBase, derived.getDifferential().getElement(), diffStart, diffEnd); +// if (edMatch == null) { +// ElementDefinition edd = derived.getDifferential().addElement(); +// edd.setPath(edBase.getPath()); +// edd.setMax("0"); +// } else { +// closeChildren(base, edBase, derived, edMatch); +// } +// } +// } +// } - private void closeChildren(StructureDefinition base, ElementDefinition edb, StructureDefinition derived, ElementDefinition edm) { - String path = edb.getPath()+"."; - int baseStart = base.getSnapshot().getElement().indexOf(edb); - int baseEnd = findEnd(base.getSnapshot().getElement(), edb, baseStart+1); - int diffStart = derived.getDifferential().getElement().indexOf(edm); - int diffEnd = findEnd(derived.getDifferential().getElement(), edm, diffStart+1); - - for (int cBase = baseStart; cBase < baseEnd; cBase++) { - ElementDefinition edBase = base.getSnapshot().getElement().get(cBase); - if (isImmediateChild(edBase, edb)) { - ElementDefinition edMatch = getMatchInDerived(edBase, derived.getDifferential().getElement(), diffStart, diffEnd); - if (edMatch == null) { - ElementDefinition edd = derived.getDifferential().addElement(); - edd.setPath(edBase.getPath()); - edd.setMax("0"); - } else { - closeChildren(base, edBase, derived, edMatch); - } - } - } - } +// +// +// +// private int findEnd(List list, ElementDefinition ed, int cursor) { +// String path = ed.getPath()+"."; +// while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { +// cursor++; +// } +// return cursor; +// } +// +// +// private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { +// for (ElementDefinition t : list) { +// if (t.getPath().equals(ed.getPath())) { +// return t; +// } +// } +// return null; +// } +// +// private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { +// for (int i = start; i < end; i++) { +// ElementDefinition t = list.get(i); +// if (t.getPath().equals(ed.getPath())) { +// return t; +// } +// } +// return null; +// } +// +// +// private boolean isImmediateChild(ElementDefinition ed) { +// String p = ed.getPath(); +// if (!p.contains(".")) { +// return false; +// } +// p = p.substring(p.indexOf(".")+1); +// return !p.contains("."); +// } +// +// private boolean isImmediateChild(ElementDefinition candidate, ElementDefinition base) { +// String p = candidate.getPath(); +// if (!p.contains(".")) +// return false; +// if (!p.startsWith(base.getPath()+".")) +// return false; +// p = p.substring(base.getPath().length()+1); +// return !p.contains("."); +// } - - - - private int findEnd(List list, ElementDefinition ed, int cursor) { - String path = ed.getPath()+"."; - while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { - cursor++; - } - return cursor; - } - - - private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { - for (ElementDefinition t : list) { - if (t.getPath().equals(ed.getPath())) { - return t; - } - } - return null; - } - - private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { - for (int i = start; i < end; i++) { - ElementDefinition t = list.get(i); - if (t.getPath().equals(ed.getPath())) { - return t; - } - } - return null; - } - - - private boolean isImmediateChild(ElementDefinition ed) { - String p = ed.getPath(); - if (!p.contains(".")) { - return false; - } - p = p.substring(p.indexOf(".")+1); - return !p.contains("."); - } - - private boolean isImmediateChild(ElementDefinition candidate, ElementDefinition base) { - String p = candidate.getPath(); - if (!p.contains(".")) - return false; - if (!p.startsWith(base.getPath()+".")) - return false; - p = p.substring(base.getPath().length()+1); - return !p.contains("."); - } - - public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set outputTracker, RenderingContext rc) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); - gen.setTranslator(getTranslator()); - TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true); - - boolean deep = false; - String m = ""; - boolean vdeep = false; - if (ed.getSnapshot().getElementFirstRep().getIsModifier()) - m = "modifier_"; - for (ElementDefinition eld : ed.getSnapshot().getElement()) { - deep = deep || eld.getPath().contains("Extension.extension."); - vdeep = vdeep || eld.getPath().contains("Extension.extension.extension."); - } - Row r = gen.new Row(); - model.getRows().add(r); - String en; - if (!full) - en = ed.getName(); - else if (ed.getSnapshot().getElement().get(0).getIsModifier()) - en = "modifierExtension"; - else - en = "extension"; - - r.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), en, null, null)); - r.getCells().add(gen.new Cell()); - r.getCells().add(gen.new Cell(null, null, describeCardinality(ed.getSnapshot().getElement().get(0), null, new UnusedTracker()), null, null)); - - ElementDefinition ved = null; - if (full || vdeep) { - r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - - r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX : HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - List children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); - for (ElementDefinition child : children) - if (!child.getPath().endsWith(".id")) { - List sdl = new ArrayList<>(); - sdl.add(ed); - genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc, "", ed); - } - } else if (deep) { - List children = new ArrayList(); - for (ElementDefinition ted : ed.getSnapshot().getElement()) { - if (ted.getPath().equals("Extension.extension")) - children.add(ted); - } - - r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - r.setIcon("icon_"+m+"extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - - for (ElementDefinition c : children) { - ved = getValueFor(ed, c); - ElementDefinition ued = getUrlFor(ed, c); - if (ved != null && ued != null) { - Row r1 = gen.new Row(); - r.getSubRows().add(r1); - r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#"+ed.getId()+"."+c.getId(), ((UriType) ued.getFixed()).getValue(), null, null)); - r1.getCells().add(gen.new Cell()); - r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); - genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); - r1.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc); - } - } - } else { - for (ElementDefinition ted : ed.getSnapshot().getElement()) { - if (ted.getPath().startsWith("Extension.value")) - ved = ted; - } - - genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false); - - r.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - } - Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); - Piece cc = gen.new Piece(null, ed.getName()+": ", null); - c.addPiece(gen.new Piece("br")).addPiece(cc); - c.addMarkdown(ed.getDescription()); - - if (!full && !(deep || vdeep) && ved != null && ved.hasBinding()) { - c.addPiece(gen.new Piece("br")); - BindingResolution br = pkp.resolveBinding(ed, ved.getBinding(), ved.getPath()); - c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); - if (ved.getBinding().hasStrength()) { - c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, " (", null))); - c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition()))); - c.getPieces().add(gen.new Piece(null, ")", null)); - } - if (ved.getBinding().hasDescription() && MarkDownProcessor.isSimpleMarkdown(ved.getBinding().getDescription())) { - c.getPieces().add(gen.new Piece(null, ": ", null)); - c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, ved.getBinding().getDescriptionElement()).asStringValue()); - } - } - c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null)); - r.getCells().add(c); - - try { - return gen.generate(model, corePath, 0, outputTracker); - } catch (org.hl7.fhir.exceptions.FHIRException e) { - throw new FHIRException(e.getMessage(), e); - } - } +// public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set outputTracker, RenderingContext rc) throws IOException, FHIRException { +// HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); +// gen.setTranslator(getTranslator()); +// TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true); +// +// boolean deep = false; +// String m = ""; +// boolean vdeep = false; +// if (ed.getSnapshot().getElementFirstRep().getIsModifier()) +// m = "modifier_"; +// for (ElementDefinition eld : ed.getSnapshot().getElement()) { +// deep = deep || eld.getPath().contains("Extension.extension."); +// vdeep = vdeep || eld.getPath().contains("Extension.extension.extension."); +// } +// Row r = gen.new Row(); +// model.getRows().add(r); +// String en; +// if (!full) +// en = ed.getName(); +// else if (ed.getSnapshot().getElement().get(0).getIsModifier()) +// en = "modifierExtension"; +// else +// en = "extension"; +// +// r.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), en, null, null)); +// r.getCells().add(gen.new Cell()); +// r.getCells().add(gen.new Cell(null, null, describeCardinality(ed.getSnapshot().getElement().get(0), null, new UnusedTracker()), null, null)); +// +// ElementDefinition ved = null; +// if (full || vdeep) { +// r.getCells().add(gen.new Cell("", "", "Extension", null, null)); +// +// r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX : HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); +// List children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); +// for (ElementDefinition child : children) +// if (!child.getPath().endsWith(".id")) { +// List sdl = new ArrayList<>(); +// sdl.add(ed); +// genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc, "", ed); +// } +// } else if (deep) { +// List children = new ArrayList(); +// for (ElementDefinition ted : ed.getSnapshot().getElement()) { +// if (ted.getPath().equals("Extension.extension")) +// children.add(ted); +// } +// +// r.getCells().add(gen.new Cell("", "", "Extension", null, null)); +// r.setIcon("icon_"+m+"extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); +// +// for (ElementDefinition c : children) { +// ved = getValueFor(ed, c); +// ElementDefinition ued = getUrlFor(ed, c); +// if (ved != null && ued != null) { +// Row r1 = gen.new Row(); +// r.getSubRows().add(r1); +// r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#"+ed.getId()+"."+c.getId(), ((UriType) ued.getFixed()).getValue(), null, null)); +// r1.getCells().add(gen.new Cell()); +// r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); +// genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); +// r1.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); +// generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc); +// } +// } +// } else { +// for (ElementDefinition ted : ed.getSnapshot().getElement()) { +// if (ted.getPath().startsWith("Extension.value")) +// ved = ted; +// } +// +// genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false); +// +// r.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); +// } +// Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); +// Piece cc = gen.new Piece(null, ed.getName()+": ", null); +// c.addPiece(gen.new Piece("br")).addPiece(cc); +// c.addMarkdown(ed.getDescription()); +// +// if (!full && !(deep || vdeep) && ved != null && ved.hasBinding()) { +// c.addPiece(gen.new Piece("br")); +// BindingResolution br = pkp.resolveBinding(ed, ved.getBinding(), ved.getPath()); +// c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); +// c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); +// if (ved.getBinding().hasStrength()) { +// c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, " (", null))); +// c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition()))); +// c.getPieces().add(gen.new Piece(null, ")", null)); +// } +// if (ved.getBinding().hasDescription() && MarkDownProcessor.isSimpleMarkdown(ved.getBinding().getDescription())) { +// c.getPieces().add(gen.new Piece(null, ": ", null)); +// c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, ved.getBinding().getDescriptionElement()).asStringValue()); +// } +// } +// c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null)); +// r.getCells().add(c); +// +// try { +// return gen.generate(model, corePath, 0, outputTracker); +// } catch (org.hl7.fhir.exceptions.FHIRException e) { +// throw new FHIRException(e.getMessage(), e); +// } +// } private ElementDefinition getUrlFor(StructureDefinition ed, ElementDefinition c) { int i = ed.getSnapshot().getElement().indexOf(c) + 1; @@ -2739,282 +2640,19 @@ public class ProfileUtilities extends TranslatingUtilities { } return null; } - - private ElementDefinition getValueFor(StructureDefinition ed, ElementDefinition c) { - int i = ed.getSnapshot().getElement().indexOf(c) + 1; - while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { - if (ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".value")) - return ed.getSnapshot().getElement().get(i); - i++; - } - return null; - } - - private static final int AGG_NONE = 0; - private static final int AGG_IND = 1; - private static final int AGG_GR = 2; - private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; - public static final String CONSTRAINT_CHAR = "C"; - - private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean mustSupportMode) { - Cell c = gen.new Cell(); - r.getCells().add(c); - if (e.hasContentReference()) { - ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), e.getContentReference(), profile); - if (ed == null) - c.getPieces().add(gen.new Piece(null, translate("sd.table", "Unknown reference to %s", e.getContentReference()), null)); - else { - if (ed.getSource() == profile) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "See ", ed.getElement().getPath()), null)); - c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), tail(ed.getElement().getPath()), ed.getElement().getPath())); - } else { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "See ", ed.getElement().getPath()), null)); - c.getPieces().add(gen.new Piece(pfx(corePath, ed.getSource().getUserString("path"))+"#"+ed.getElement().getPath(), tail(ed.getElement().getPath())+" ("+ed.getSource().getTypeName()+")", ed.getElement().getPath())); - } - } - return c; - } - List types = e.getType(); - if (!e.hasType()) { - if (root) { // we'll use base instead of types then - StructureDefinition bsd = profile == null ? null : context.fetchResource(StructureDefinition.class, profile.getBaseDefinition(), profile); - 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)); - } else { - c.getPieces().add(gen.new Piece(null, bsd.getName(), null)); - } - } - return c; - } else if (e.hasContentReference()) { - return c; - } else { - ElementDefinition d = (ElementDefinition) e.getUserData(DERIVATION_POINTER); - if (d != null && d.hasType()) { - types = new ArrayList(); - for (TypeRefComponent tr : d.getType()) { - TypeRefComponent tt = tr.copy(); - tt.setUserData(DERIVATION_EQUALS, true); - types.add(tt); - } - } else { - return c; - } - } - } - - boolean first = true; - - TypeRefComponent tl = null; - for (TypeRefComponent t : types) { - if (!mustSupportMode || allTypesMustSupport(e) || isMustSupport(t)) { - if (first) { - first = false; - } else { - c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); - } - tl = t; - if (t.hasTarget()) { - c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); - if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - c.getPieces().add(gen.new Piece(null, "(", null)); - boolean tfirst = true; - for (CanonicalType u : t.getTargetProfile()) { - if (!mustSupportMode || allProfilesMustSupport(t.getTargetProfile()) || isMustSupport(u)) { - if (tfirst) - tfirst = false; - else - c.addPiece(gen.new Piece(null, " | ", null)); - genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue(), null); - if (!mustSupportMode && isMustSupport(u) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); - } - } - } - c.getPieces().add(gen.new Piece(null, ")", null)); - if (t.getAggregation().size() > 0) { - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); - boolean firstA = true; - for (Enumeration a : t.getAggregation()) { - if (firstA = true) - firstA = false; - else - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); - } - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); - } - } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type - String ref; - boolean pfirst = true; - for (CanonicalType p : t.getProfile()) { - if (!mustSupportMode || allProfilesMustSupport(t.getProfile()) || isMustSupport(p)) { - if (pfirst) { - pfirst = false; - } else { - c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); - } - - 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:")) { - if (p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) { - String pp = p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT); - pp = pp.substring(pp.indexOf(".")); - c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1]+pp, t.getWorkingCode()))); - } else { - c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); - } - } else { - c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); - } - } else { - c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); - } - if (!mustSupportMode && isMustSupport(p) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); - } - } - } - } else { - String tc = t.getWorkingCode(); - if (Utilities.isAbsoluteUrl(tc)) { - StructureDefinition sd = context.fetchTypeDefinition(tc); - if (sd == null) { - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); - } else { - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), sd.getTypeName(), null))); - } - } else if (pkp != null && pkp.hasLinkFor(tc)) { - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); - } else { - c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); - } - if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - } - } - } - return c; - } - - - private String pfx(String prefix, String url) { - return Utilities.isAbsoluteUrl(url) ? url : prefix + url; - } - - public void genTargetLink(HierarchicalTableGenerator gen, String profileBaseFileName, String corePath, Cell c, TypeRefComponent t, String u, Resource src) { - if (u.startsWith("http://hl7.org/fhir/StructureDefinition/")) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, u, src); - if (sd != null) { - String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); - c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getUserString("path")), disp, null))); - } else { - String rn = u.substring(40); - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, rn), rn, null))); - } - } else if (Utilities.isAbsoluteUrl(u)) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, u, src); - if (sd != null && pkp != null) { - String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); - String ref = pkp.getLinkForProfile(null, sd.getUrl()); - if (ref != null && ref.contains("|")) - ref = ref.substring(0, ref.indexOf("|")); - c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null))); - } else - c.addPiece(checkForNoChange(t, gen.new Piece(null, u, null))); - } else if (t.hasTargetProfile() && u.startsWith("#")) - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.substring(1).toLowerCase()+".html", u, null))); - } - - private boolean isProfiledType(List theProfile) { - for (CanonicalType next : theProfile){ - if (StringUtils.defaultString(next.getValueAsString()).contains(":")) { - return true; - } - } - return false; - } - - - public static String codeForAggregation(AggregationMode a) { - switch (a) { - case BUNDLED : return "b"; - case CONTAINED : return "c"; - case REFERENCED: return "r"; - default: return "?"; - } - } - - public static String hintForAggregation(AggregationMode a) { - if (a != null) - return a.getDefinition(); - else - return null; - } - - - private String checkPrepend(String corePath, String path) { - if (pkp != null && pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) - return corePath+path; - else - return path; - } - - - private class ElementInStructure { - - private StructureDefinition source; - private ElementDefinition element; - - public ElementInStructure(StructureDefinition source, ElementDefinition ed) { - this.source = source; - this.element = ed; - } - - public StructureDefinition getSource() { - return source; - } - - public ElementDefinition getElement() { - return element; - } - - } - private ElementInStructure getElementByName(List elements, String contentReference, StructureDefinition source) { - if (contentReference.contains("#")) { - String url = contentReference.substring(0, contentReference.indexOf("#")); - contentReference = contentReference.substring(contentReference.indexOf("#")); - if (Utilities.noString(url)) { - url = source.getUrl(); - } - if (!url.equals(source.getUrl())) { - source = context.fetchResource(StructureDefinition.class, url, source); - if (source == null) { - throw new FHIRException("Unable to resolve StructureDefinition "+url+" resolving content reference "+contentReference); - } - elements = source.getSnapshot().getElement(); - } - } - for (ElementDefinition ed : elements) { - if (("#"+ed.getPath()).equals(contentReference)) { - return new ElementInStructure(source, ed); - } - if (("#"+ed.getId()).equals(contentReference)) { - return new ElementInStructure(source, ed); - } - } - throw new Error("getElementByName: can't find "+contentReference+" in "+elements.toString()+" from "+source.getUrl()); +// +// private ElementDefinition getValueFor(StructureDefinition ed, ElementDefinition c) { +// int i = ed.getSnapshot().getElement().indexOf(c) + 1; +// while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { +// if (ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".value")) +// return ed.getSnapshot().getElement().get(i); +// i++; +// } // return null; - } +// } + + + protected ElementDefinitionResolution getElementById(StructureDefinition source, List elements, String contentReference) { if (!contentReference.startsWith("#") && contentReference.contains("#")) { @@ -3034,1612 +2672,44 @@ public class ProfileUtilities extends TranslatingUtilities { return null; } - - public static String describeExtensionContext(StructureDefinition ext) { - StringBuilder b = new StringBuilder(); - b.append("Use on "); - for (int i = 0; i < ext.getContext().size(); i++) { - StructureDefinitionContextComponent ec = ext.getContext().get(i); - if (i > 0) - b.append(i < ext.getContext().size() - 1 ? ", " : " or "); - b.append(ec.getType().getDisplay()); - b.append(" "); - b.append(ec.getExpression()); - } - if (ext.hasContextInvariant()) { - b.append(", with Context Invariant = "); - boolean first = true; - for (StringType s : ext.getContextInvariant()) { - if (first) - first = false; - else - b.append(", "); - b.append(""+s.getValue()+""); - } - } - return b.toString(); - } - - private String describeCardinality(ElementDefinition definition, ElementDefinition fallback, UnusedTracker tracker) { - IntegerType min = definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); - StringType max = definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); - if (min.isEmpty() && fallback != null) - min = fallback.getMinElement(); - if (max.isEmpty() && fallback != null) - max = fallback.getMaxElement(); - - tracker.used = !max.isEmpty() && !max.getValue().equals("0"); - - if (min.isEmpty() && max.isEmpty()) - return null; - else - return (!min.hasValue() ? "" : Integer.toString(min.getValue())) + ".." + (!max.hasValue() ? "" : max.getValue()); - } - - private Cell genCardinality(HierarchicalTableGenerator gen, ElementDefinition definition, Row row, boolean hasDef, UnusedTracker tracker, ElementDefinition fallback) { - IntegerType min = !hasDef ? new IntegerType() : definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); - StringType max = !hasDef ? new StringType() : definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); - if (min.isEmpty() && definition.getUserData(DERIVATION_POINTER) != null) { - ElementDefinition base = (ElementDefinition) definition.getUserData(DERIVATION_POINTER); - if (base.hasMinElement()) { - min = base.getMinElement().copy(); - min.setUserData(DERIVATION_EQUALS, true); - } - } - if (max.isEmpty() && definition.getUserData(DERIVATION_POINTER) != null) { - ElementDefinition base = (ElementDefinition) definition.getUserData(DERIVATION_POINTER); - if (base.hasMaxElement()) { - max = base.getMaxElement().copy(); - max.setUserData(DERIVATION_EQUALS, true); - } - } - if (min.isEmpty() && fallback != null) - min = fallback.getMinElement(); - if (max.isEmpty() && fallback != null) - max = fallback.getMaxElement(); - - if (!max.isEmpty()) - tracker.used = !max.getValue().equals("0"); - - String hint = null; - if (max.hasValue() && min.hasValue() && "*".equals(max.getValue()) && 0 == min.getValue()) { - if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { - String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); - if ("present".equals(code)) { - hint = "This element is present as a JSON Array even when there are no items in the instance"; - } else { - hint = "This element may be present as a JSON Array even when there are no items in the instance"; - } - } - } - Cell cell = gen.new Cell(null, null, null, null, null); - row.getCells().add(cell); - if (!min.isEmpty() || !max.isEmpty()) { - cell.addPiece(checkForNoChange(min, gen.new Piece(null, !min.hasValue() ? "" : Integer.toString(min.getValue()), hint))); - cell.addPiece(checkForNoChange(min, max, gen.new Piece(null, "..", hint))); - cell.addPiece(checkForNoChange(max, gen.new Piece(null, !max.hasValue() ? "" : max.getValue(), hint))); - } - return cell; - } - - - private Piece checkForNoChange(Element source, Piece piece) { - if (source.hasUserData(DERIVATION_EQUALS)) { - piece.addStyle("opacity: 0.5"); - } - return piece; - } - - private String checkForNoChange(Element source) { - if (source.hasUserData(DERIVATION_EQUALS)) { - return "opacity: 0.5"; - } else { - return null; - } - } - - - private Piece applyAsUnchanged(Piece piece) { - piece.addStyle("opacity: 0.5"); - return piece; - } - - private String applyAsUnchanged() { - return "opacity: 0.5"; - } - - - private Piece checkForNoChange(Element src1, Element src2, Piece piece) { - if (src1.hasUserData(DERIVATION_EQUALS) && src2.hasUserData(DERIVATION_EQUALS)) { - piece.addStyle("opacity: 0.5"); - } - return piece; - } - - public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, - boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException { - return generateTable(defFile, profile, diff, imageFolder, inlineGraphics, profileBaseFileName, snapshot, corePath, imagePath, logicalModel, allInvariants, outputTracker, mustSupport, rc, ""); - } - - public List supplementMissingDiffElements(StructureDefinition profile) { - List list = new ArrayList<>(); - list.addAll(profile.getDifferential().getElement()); - if (list.isEmpty()) { - ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); - root.setId(profile.getTypeName()); - list.add(root); - } else { - if (list.get(0).getPath().contains(".")) { - ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); - root.setId(profile.getTypeName()); - list.add(0, root); - } - } - insertMissingSparseElements(list); - return list; - } - - public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, - boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException { - assert(diff != snapshot);// check it's ok to get rid of one of these - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); - gen.setTranslator(getTranslator()); - TableModel model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER); - List list; - if (diff) - list = supplementMissingDiffElements(profile); - else { - list = new ArrayList<>(); - list.addAll(profile.getSnapshot().getElement()); - } - List profiles = new ArrayList(); - profiles.add(profile); - keyRows.clear(); - - genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile); - try { - return gen.generate(model, imagePath, 0, outputTracker); - } catch (org.hl7.fhir.exceptions.FHIRException e) { - throw new FHIRException(context.formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); - } - } - - - private void insertMissingSparseElements(List list) { - int i = 1; - while (i < list.size()) { - String[] pathCurrent = list.get(i).getPath().split("\\."); - String[] pathLast = list.get(i-1).getPath().split("\\."); - int firstDiff = 0; // the first entry must be a match - while (firstDiff < pathCurrent.length && firstDiff < pathLast.length && pathCurrent[firstDiff].equals(pathLast[firstDiff])) { - firstDiff++; - } - if (!(isSibling(pathCurrent, pathLast, firstDiff) || isChild(pathCurrent, pathLast, firstDiff))) { - // now work backwards down to lastMatch inserting missing path nodes - ElementDefinition parent = findParent(list, i, list.get(i).getPath()); - int parentDepth = Utilities.charCount(parent.getPath(), '.')+1; - int childDepth = Utilities.charCount(list.get(i).getPath(), '.')+1; - if (childDepth > parentDepth + 1) { - String basePath = parent.getPath(); - String baseId = parent.getId(); - for (int index = parentDepth; index >= firstDiff; index--) { - String mtail = makeTail(pathCurrent, parentDepth, index); - ElementDefinition root = new ElementDefinition().setPath(basePath+"."+mtail); - root.setId(baseId+"."+mtail); - list.add(i, root); - } - } - } - i++; - } - } - - private ElementDefinition findParent(List list, int i, String path) { - while (i > 0 && !path.startsWith(list.get(i).getPath()+".")) { - i--; - } - return list.get(i); - } - - private boolean isSibling(String[] pathCurrent, String[] pathLast, int firstDiff) { - return pathCurrent.length == pathLast.length && firstDiff == pathCurrent.length-1; - } - - - private boolean isChild(String[] pathCurrent, String[] pathLast, int firstDiff) { - return pathCurrent.length == pathLast.length+1 && firstDiff == pathLast.length; - } - - private String makeTail(String[] pathCurrent, int start, int index) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("."); - for (int i = start; i <= index; i++) { - b.append(pathCurrent[i]); - } - return b.toString(); - } - - private String makePath(String[] pathCurrent, int index) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("."); - for (int i = 0; i <= index; i++) { - b.append(pathCurrent[i]); - } - return b.toString(); - } - - - public XhtmlNode generateGrid(String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, String corePath, String imagePath, Set outputTracker) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); - gen.setTranslator(getTranslator()); - TableModel model = gen.initGridTable(corePath, profile.getId()); - List list = profile.getSnapshot().getElement(); - List profiles = new ArrayList(); - profiles.add(profile); - genGridElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, true, profileBaseFileName, null, corePath, imagePath, true, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list)); - try { - return gen.generate(model, imagePath, 1, outputTracker); - } catch (org.hl7.fhir.exceptions.FHIRException e) { - throw new FHIRException(e.getMessage(), e); - } - } - - - private boolean usesMustSupport(List list) { - for (ElementDefinition ed : list) - if (ed.hasMustSupport() && ed.getMustSupport()) - return true; - return false; - } - - - private Row genElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, - boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, Resource srcSD) throws IOException, FHIRException { - Row originalRow = slicingRow; - StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); - Row typesRow = null; - - List children = getChildren(all, element); -// if (!snapshot && isExtension && extensions != null && extensions != isExtension) -// return; - - if (!onlyInformationIsMapping(all, element)) { - Row row = gen.new Row(); - row.setAnchor(element.getPath()); - row.setColor(getRowColor(element, isConstraintMode)); - if (element.hasSlicing()) - row.setLineColor(1); - else if (element.hasSliceName()) - row.setLineColor(2); - else - row.setLineColor(0); - boolean hasDef = element != null; - boolean ext = false; - if (tail(element.getPath()).equals("extension")) { - if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) - row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - else - row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - ext = true; - } else if (tail(element.getPath()).equals("modifierExtension")) { - if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) - row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - else - row.setIcon("icon_modifier_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - } else if (!hasDef || element.getType().size() == 0) { - if (root && context.getResourceNames().contains(profile.getType())) { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { - row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); - keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); - } else { - row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - } - } else if (hasDef && element.getType().size() > 1) { - if (allAreReference(element.getType())) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - } else if (element.hasExtension(ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); - } else { - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); - typesRow = row; - } - } else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { - row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); - } else if (hasDef && isPrimitive(element.getType().get(0).getWorkingCode())) { - if (keyRows.contains(element.getId())) { - row.setIcon("icon-key.png", HierarchicalTableGenerator.TEXT_ICON_KEY); - } else { - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - } - } else if (hasDef && element.getType().get(0).hasTarget()) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - } else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) { - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { - row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); - keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); - } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { - row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - } else { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - } - if (element.hasUserData("render.opaque")) { - row.setOpacity("0.5"); - } - UnusedTracker used = new UnusedTracker(); - String ref = defPath == null ? null : defPath + anchorPrefix + element.getId(); - String sName = tail(element.getPath()); - if (element.hasSliceName()) - sName = sName +":"+element.getSliceName(); - used.used = true; - 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, all); - genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc); - 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(); - showMissing = false; //? - } else { - row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); - slicingRow = row; - for (Cell cell : row.getCells()) - for (Piece p : cell.getPieces()) { - p.addStyle("font-style: italic"); - } - } - } else if (element.hasSliceName()) { - row.setIcon("icon_slice_item.png", HierarchicalTableGenerator.TEXT_ICON_SLICE_ITEM); - } - if (used.used || showMissing) - rows.add(row); - if (!used.used && !element.hasSlicing()) { - for (Cell cell : row.getCells()) - for (Piece p : cell.getPieces()) { - p.setStyle("text-decoration:line-through"); - p.setReference(null); - } - } else { - if (slicingRow != originalRow && !children.isEmpty()) { - // we've entered a slice; we're going to create a holder row for the slice children - Row hrow = gen.new Row(); - hrow.setAnchor(element.getPath()); - hrow.setColor(getRowColor(element, isConstraintMode)); - hrow.setLineColor(1); - hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - hrow.getCells().add(gen.new Cell(null, null, sName+":All Slices", "", null)); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all slices", "", null)); - row.getSubRows().add(hrow); - row = hrow; - } - if (typesRow != null && !children.isEmpty()) { - // we've entered a typing slice; we're going to create a holder row for the all types children - Row hrow = gen.new Row(); - hrow.setAnchor(element.getPath()); - hrow.setColor(getRowColor(element, isConstraintMode)); - hrow.setLineColor(1); - hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - hrow.getCells().add(gen.new Cell(null, null, sName+":All Types", "", null)); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all Types", "", null)); - row.getSubRows().add(hrow); - row = hrow; - } - - Row currRow = row; - List groups = readChoices(element, children); - boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension"); - if (!element.prohibited()) { - for (ElementDefinition child : children) { - if (!child.hasSliceName()) { - currRow = row; - } - Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); - - if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { - currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix, srcSD); - } - } - } -// if (!snapshot && (extensions == null || !extensions)) -// for (ElementDefinition child : children) -// if (child.getPath().endsWith(".extension") || child.getPath().endsWith(".modifierExtension")) -// genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); - } - if (typesRow != null && !element.prohibited()) { - makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport, srcSD); - } - } - return slicingRow; - } - - private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) { - String name = tail(element.getPath()); - for (ElementChoiceGroup grp : groups) { - if (grp.getElements().contains(name)) { - if (grp.getRow() == null) { - grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode)); - } - return grp.getRow(); - } - } - return row; - } - - private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) { - Row row = gen.new Row(); - row.setAnchor(parent.getPath()+"-"+grp.getName()); - row.setColor(getRowColor(parent, isConstraintMode)); - row.setLineColor(1); - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); - row.getCells().add(gen.new Cell(null, null, "(Choice of one)", "", null)); - row.getCells().add(gen.new Cell()); - row.getCells().add(gen.new Cell(null, null, (grp.mandatory ? "1" : "0")+"..1", "", null)); - row.getCells().add(gen.new Cell()); - row.getCells().add(gen.new Cell()); - prow.getSubRows().add(row); - return row; - } - - public Cell genElementNameCell(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, List elements) throws IOException { - String hint = ""; - hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : "")); - if (hasDef && element.hasDefinition()) { - hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); - hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); - } - if (element.hasSlicing() && slicesExist(elements, element)) { // some elements set up slicing but don't actually slice, so we don't augment the name - sName = "Slices for "+sName; - } - Cell left = gen.new Cell(null, ref, sName, hint, null); - row.getCells().add(left); - return left; - } - - private boolean slicesExist(List elements, ElementDefinition element) { - if (elements == null) { - return true; - } - boolean found = false; - int start = elements.indexOf(element); - if (start < 0) { - return false; - } - for (int i = start; i < elements.size(); i++) { - ElementDefinition ed = elements.get(i); - if (ed.getPath().equals(element.getPath())) { - if (ed.hasSliceName()) { - found = true; - } - } - if (ed.getPath().length() < element.getPath().length()) { - break; - } - } - return found; - } - - 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, boolean allowSubRows, RenderingContext rc) throws IOException { - List res = new ArrayList<>(); - Cell gc = gen.new Cell(); - row.getCells().add(gc); - res.add(gc); - if (element != null && element.getIsModifier()) { - checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false)); - } - if (element != null && element.getMustSupport()) { - checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false)); - } - if (element != null && element.getIsSummary()) { - checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); - } - if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { - Piece p = gc.addText(ProfileUtilities.CONSTRAINT_CHAR); - p.setHint(translate("sd.table", "This element has or is affected by constraints ("+listConstraintsAndConditions(element)+")")); - p.addStyle(CONSTRAINT_STYLE); - p.setReference(Utilities.pathURL(VersionUtilities.getSpecUrl(context.getVersion()), "conformance-rules.html#constraints")); - } - if (element != null && element.hasExtension(ToolingExtensions.EXT_STANDARDS_STATUS)) { - StandardsStatus ss = StandardsStatus.fromCode(element.getExtensionString(ToolingExtensions.EXT_STANDARDS_STATUS)); - gc.addStyledText("Standards Status = "+ss.toDisplay(), ss.getAbbrev(), "black", ss.getColor(), context.getSpecUrl()+"versions.html#std-process", true); - } - - ExtensionContext extDefn = null; - if (ext) { - if (element != null && element.getType().size() == 1 && element.getType().get(0).hasProfile()) { - String eurl = element.getType().get(0).getProfile().get(0).getValue(); - extDefn = locateExtension(StructureDefinition.class, eurl); - 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, allowSubRows, rc)); - } else { - String name = element.hasSliceName() ? element.getSliceName() : urltail(eurl); - nameCell.getPieces().get(0).setText(name); - // left.getPieces().get(0).setReference((String) extDefn.getExtensionStructure().getTag("filename")); - nameCell.getPieces().get(0).setHint(translate("sd.table", "Extension URL")+" = "+extDefn.getUrl()); - res.add(genCardinality(gen, element, row, hasDef, used, extDefn.getElement())); - ElementDefinition valueDefn = extDefn.getExtensionValueDefinition(); - if (valueDefn != null && !"0".equals(valueDefn.getMax())) - res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); - 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, allowSubRows, rc)); - } - } else { - res.add(genCardinality(gen, element, row, hasDef, used, null)); - if ("0".equals(element.getMax())) - 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, allowSubRows, rc)); - } - } else { - res.add(genCardinality(gen, element, row, hasDef, used, null)); - if (hasDef && !"0".equals(element.getMax()) && typesRow == null) - 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, allowSubRows, rc)); - } - return res; - } - - - private Cell addCell(Row row, Cell cell) { - row.getCells().add(cell); - return (cell); - } - - private String checkAdd(String src, String app) { - return app == null ? src : src + app; - } - - private boolean hasNonBaseConditions(List conditions) { - for (IdType c : conditions) { - if (!isBaseCondition(c)) { - return true; - } - } - return false; - } - - - private boolean hasNonBaseConstraints(List constraints) { - for (ElementDefinitionConstraintComponent c : constraints) { - if (!isBaseConstraint(c)) { - return true; - } - } - return false; - } - - private String listConstraintsAndConditions(ElementDefinition element) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (ElementDefinitionConstraintComponent con : element.getConstraint()) { - if (!isBaseConstraint(con)) { - b.append(con.getKey()); - } - } - for (IdType id : element.getCondition()) { - if (!isBaseCondition(id)) { - b.append(id.asStringValue()); - } - } - return b.toString(); - } - - private boolean isBaseCondition(IdType c) { - String key = c.asStringValue(); - 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 != 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, Resource src) { - // create a child for each choice - for (TypeRefComponent tr : element.getType()) { - if (!mustSupportMode || allTypesMustSupport(element) || isMustSupport(tr)) { - Row choicerow = gen.new Row(); - String t = tr.getWorkingCode(); - if (isReference(t)) { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); - choicerow.getCells().add(gen.new Cell()); - choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - Cell c = gen.new Cell(); - choicerow.getCells().add(c); - if (ADD_REFERENCE_TO_TABLE) { - if (tr.getWorkingCode().equals("canonical")) - c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); - else - c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); - if (!mustSupportMode && isMustSupportDirect(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - c.getPieces().add(gen.new Piece(null, "(", null)); - } - boolean first = true; - for (CanonicalType rt : tr.getTargetProfile()) { - if (!mustSupportMode || allProfilesMustSupport(tr.getTargetProfile()) || isMustSupport(rt)) { - if (!first) - c.getPieces().add(gen.new Piece(null, " | ", null)); - genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue(), src); - if (!mustSupportMode && isMustSupport(rt) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); - } - first = false; - } - } - if (first) { - c.getPieces().add(gen.new Piece(null, "Any", null)); - } - - if (ADD_REFERENCE_TO_TABLE) { - c.getPieces().add(gen.new Piece(null, ")", null)); - } - - } else { - StructureDefinition sd = context.fetchTypeDefinition(t); - if (sd == null) { - System.out.println("Unable to find "+t); - sd = context.fetchTypeDefinition(t); - } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); - choicerow.getCells().add(gen.new Cell()); - choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getTypeName(), null, null); - choicerow.getCells().add(c); - if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - } else { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); - choicerow.getCells().add(gen.new Cell()); - choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getTypeName(), null, null); - choicerow.getCells().add(c); - if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - } - if (tr.hasProfile()) { - Cell typeCell = choicerow.getCells().get(3); - typeCell.addPiece(gen.new Piece(null, "(", null)); - boolean first = true; - for (CanonicalType pt : tr.getProfile()) { - if (!mustSupportMode || allProfilesMustSupport(tr.getProfile()) || isMustSupport(pt)) { - if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); - StructureDefinition psd = context.fetchResource(StructureDefinition.class, pt.getValue(), src); - if (psd == null) - typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); - else - typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); - if (!mustSupportMode && isMustSupport(pt) && element.getMustSupport()) { - typeCell.addPiece(gen.new Piece(null, " ", null)); - typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); - } - } - } - typeCell.addPiece(gen.new Piece(null, ")", null)); - } - } - choicerow.getCells().add(gen.new Cell()); - subRows.add(choicerow); - } - } - } - - private boolean isReference(String t) { - return t.equals("Reference") || t.equals("canonical"); - } - - - - private void genGridElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, String corePath, String imagePath, boolean root, boolean isConstraintMode) throws IOException, FHIRException { - StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); - String s = tail(element.getPath()); - List children = getChildren(all, element); - boolean isExtension = (s.equals("extension") || s.equals("modifierExtension")); - - if (!onlyInformationIsMapping(all, element)) { - Row row = gen.new Row(); - row.setAnchor(element.getPath()); - row.setColor(getRowColor(element, isConstraintMode)); - if (element.hasSlicing()) - row.setLineColor(1); - else if (element.hasSliceName()) - row.setLineColor(2); - else - row.setLineColor(0); - boolean hasDef = element != null; - String ref = defPath == null ? null : defPath + element.getId(); - UnusedTracker used = new UnusedTracker(); - used.used = true; - Cell left = gen.new Cell(); - if (element.getType().size() == 1 && element.getType().get(0).isPrimitive()) - left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement())).addStyle("font-weight:bold")); - else - left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement()))); - if (element.hasSliceName()) { - left.getPieces().add(gen.new Piece("br")); - String indent = StringUtils.repeat('\u00A0', 1+2*(element.getPath().split("\\.").length)); - left.getPieces().add(gen.new Piece(null, indent + "("+element.getSliceName() + ")", null)); - } - row.getCells().add(left); - - ExtensionContext extDefn = null; - genCardinality(gen, element, row, hasDef, used, null); - if (hasDef && !"0".equals(element.getMax())) - genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, false); - else - row.getCells().add(gen.new Cell()); - generateGridDescription(gen, row, element, null, used.used, null, null, profile, corePath, imagePath, root, null); -/* if (element.hasSlicing()) { - if (standardExtensionSlicing(element)) { - used.used = element.hasType() && element.getType().get(0).hasProfile(); - showMissing = false; - } else { - row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); - row.getCells().get(2).getPieces().clear(); - for (Cell cell : row.getCells()) - for (Piece p : cell.getPieces()) { - p.addStyle("font-style: italic"); - } - } - }*/ - rows.add(row); - for (ElementDefinition child : children) - if (child.getMustSupport()) - genGridElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, corePath, imagePath, false, isConstraintMode); - } - } - - - private ExtensionContext locateExtension(Class class1, String value) { - if (value.contains("#")) { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); - if (ext == null) - return null; - String tail = value.substring(value.indexOf("#")+1); - ElementDefinition ed = null; - for (ElementDefinition ted : ext.getSnapshot().getElement()) { - if (tail.equals(ted.getSliceName())) { - ed = ted; - return new ExtensionContext(ext, ed); - } - } - return null; - } else { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value); - if (ext == null) - return null; - else - return new ExtensionContext(ext, ext.getSnapshot().getElement().get(0)); - } - } - - - private boolean extensionIsComplex(String value) { - if (value.contains("#")) { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); - if (ext == null) - return false; - String tail = value.substring(value.indexOf("#")+1); - ElementDefinition ed = null; - for (ElementDefinition ted : ext.getSnapshot().getElement()) { - if (tail.equals(ted.getSliceName())) { - ed = ted; - break; - } - } - if (ed == null) - return false; - int i = ext.getSnapshot().getElement().indexOf(ed); - int j = i+1; - while (j < ext.getSnapshot().getElement().size() && !ext.getSnapshot().getElement().get(j).getPath().equals(ed.getPath())) - j++; - return j - i > 5; - } else { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value); - return ext != null && ext.getSnapshot().getElement().size() > 5; - } - } - - - public String getRowColor(ElementDefinition element, boolean isConstraintMode) { - switch (element.getUserInt(UD_ERROR_STATUS)) { - case STATUS_HINT: return ROW_COLOR_HINT; - case STATUS_WARNING: return ROW_COLOR_WARNING; - case STATUS_ERROR: return ROW_COLOR_ERROR; - case STATUS_FATAL: return ROW_COLOR_FATAL; - } - if (isConstraintMode && !element.getMustSupport() && !element.getIsModifier() && element.getPath().contains(".")) - return null; // ROW_COLOR_NOT_MUST_SUPPORT; - else - return null; - } - - - private String urltail(String path) { - if (path.contains("#")) - return path.substring(path.lastIndexOf('#')+1); - if (path.contains("/")) - return path.substring(path.lastIndexOf('/')+1); - else - return path; - - } - - private boolean standardExtensionSlicing(ElementDefinition element) { - String t = tail(element.getPath()); - return (t.equals("extension") || t.equals("modifierExtension")) - && 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, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { - return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc); - } - - 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, RenderingContext rc) throws IOException, FHIRException { - Cell c = gen.new Cell(); - row.getCells().add(c); - - if (used) { - if (logicalModel && ToolingExtensions.hasExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) { - if (root) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); - } else if (!root && ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace") && - !ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace").equals(ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); - } - } - if (root) { - 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.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type")+". ", null)); - - List children = new ArrayList<>(); - for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { - if (sd.hasBaseDefinition() && sd.getBaseDefinition().equals(profile.getUrl())) { - children.add(sd); - } - } - if (!children.isEmpty()) { - c.addPiece(gen.new Piece(null, "Child "+(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types")+": ", null)); - boolean first = true; - for (StructureDefinition sd : children) { - if (first) first = false; else c.addPiece(gen.new Piece(null, ", ", null)); - c.addPiece(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); - } - } - } - } - if (definition.getPath().endsWith("url") && definition.hasFixed()) { - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); - } else { - if (definition != null && definition.hasShort()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null))); - } else if (fallback != null && fallback.hasShort()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5")); - } - if (url != null) { - if (!c.getPieces().isEmpty()) - c.addPiece(gen.new Piece("br")); - String fullUrl = url.startsWith("#") ? baseURL+url : url; - StructureDefinition ed = context.fetchResource(StructureDefinition.class, url, profile); - String ref = null; - String ref2 = null; - String fixedUrl = null; - if (ed != null) { - String p = ed.getUserString("path"); - if (p != null) { - ref = p.startsWith("http:") || igmode ? p : Utilities.pathURL(corePath, p); - } - fixedUrl = getFixedUrl(ed); - if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension? - if (fixedUrl.equals(url)) - fixedUrl = null; - else { - StructureDefinition ed2 = context.fetchResource(StructureDefinition.class, fixedUrl); - if (ed2 != null) { - String p2 = ed2.getUserString("path"); - if (p2 != null) { - ref2 = p2.startsWith("http:") || igmode ? p2 : Utilities.pathURL(corePath, p2); - } - } - } - } - } - if (fixedUrl == null) { - if (!Utilities.noString(fullUrl)) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(ref, fullUrl, null)); - } - } else { - // reference to a profile take on the extension show the base URL - c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(ref2, fixedUrl, null)); - c.getPieces().add(gen.new Piece(null, translate("sd.table", " profiled by ")+" ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(ref, fullUrl, null)); - - } - } - - if (definition.hasSlicing()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); - } - if (!definition.getPath().contains(".") && ToolingExtensions.hasExtension(profile, ToolingExtensions.EXT_BINDING_STYLE)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null)); - c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null)); - c.getPieces().add(gen.new Piece(null, " binding style", null)); - - } - if (definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "When this element is read ", null)); - Piece piece = gen.new Piece("code"); - piece.addHtml(new XhtmlNode(NodeType.Text).setContent(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_IMPLIED_PREFIX))); - c.getPieces().add(piece); - c.getPieces().add(gen.new Piece(null, " is prefixed to the value before validation", null)); - } - - if (definition.hasExtension(ToolingExtensions.EXT_EXTENSION_STYLE)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - String es = definition.getExtensionString(ToolingExtensions.EXT_EXTENSION_STYLE); - if ("named-elements".equals(es)) { - if (rc.hasLink(KnownLinkType.JSON_NAMES)) { - c.getPieces().add(gen.new Piece(rc.getLink(KnownLinkType.JSON_NAMES), "This element can be extended by named JSON elements", null)); - } else { - c.getPieces().add(gen.new Piece(ToolingExtensions.WEB_EXTENSION_STYLE, "This element can be extended by named JSON elements", null)); - } - } - } - if (definition.hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) { - String ide = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_ID_EXPECTATION); - if (ide.equals("optional")) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "Id may or not be present (this is the default for elements but not resources)", null)); - } else if (ide.equals("required")) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "Id is required to be present (this is the default for resources but not elements)", null)); - } else if (ide.equals("required")) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "An ID is not allowed in this context", null)); - } - } - if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); - c.getPieces().add(gen.new Piece(null, " (", null)); - c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); - c.getPieces().add(gen.new Piece(null, ")", null)); - } else { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Element Name")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); - } - } else if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); - } - if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); - if ("present".equals(code)) { - c.getPieces().add(gen.new Piece(null, "JSON: This element is present as a JSON Array even when there are no items in the instance", null)); - } else { - c.getPieces().add(gen.new Piece(null, "JSON: This element may be present as a JSON Array even when there are no items in the instance", null)); - } - } - String jn = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_NAME); - if (!Utilities.noString(jn)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - if (definition.getPath().contains(".")) { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name")+": ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, jn, null)); - } else { - c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name for Type")+": ", null).addStyle("font-weight:bold")); - Piece piece = gen.new Piece("code"); - piece.addHtml(new XhtmlNode(NodeType.Text).setContent(jn)); - c.getPieces().add(piece); - } - } - - if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "JSON: The type of this element is inferred from the JSON type in the instance", null)); - } - if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "JSON: This object can be represented as null in the JSON structure (which counts as 'present' for cardinality purposes)", null)); - } - if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); - c.getPieces().add(gen.new Piece(null, "JSON: Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null)); - } - if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { - for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - String cond = ToolingExtensions.readStringExtension(e, "condition"); - String type = ToolingExtensions.readStringExtension(e, "type"); - c.getPieces().add(gen.new Piece(null, "JSON: If ", null)); - Piece piece = gen.new Piece("code"); - piece.addHtml(new XhtmlNode(NodeType.Text).setContent(cond)); - c.getPieces().add(piece); - c.getPieces().add(gen.new Piece(null, "then the type is ", null)); - StructureDefinition sd = context.fetchTypeDefinition(type); - if (sd == null) { - c.getPieces().add(gen.new Piece("")); - c.getPieces().add(gen.new Piece(null, type, null)); - c.getPieces().add(gen.new Piece("")); - } else { - c.getPieces().add(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); - } - } - } - if (definition != null) { - ElementDefinitionBindingComponent binding = null; - if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) - binding = makeUnifiedBinding(valueDefn.getBinding(), valueDefn); - else if (definition.hasBinding()) - binding = makeUnifiedBinding(definition.getBinding(), definition); - if (binding!=null && !binding.isEmpty()) { - if (!c.getPieces().isEmpty()) - c.addPiece(gen.new Piece("br")); - 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()) { - c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, " (", null))); - c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition()))); - c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null))); - } - if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { - c.getPieces().add(gen.new Piece(null, ": ", null)); - c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()))); - } - - AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(pkp, corePath, profile, definition.getPath(), rc, null); - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { - abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)); - } - if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) { - abr.seeMinBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MIN_VALUESET)); - } - if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { - abr.seeAdditionalBindings(binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)); - } - abr.render(gen, c); - } - for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { - if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { - if (!c.getPieces().isEmpty()) - c.addPiece(gen.new Piece("br")); - 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))); - } - } - if ((definition.hasBase() && "*".equals(definition.getBase().getMax())) || (definition.hasMax() && "*".equals(definition.getMax()))) { - if (c.getPieces().size() > 0) - c.addPiece(gen.new Piece("br")); - if (definition.hasOrderMeaning()) { - c.getPieces().add(gen.new Piece(null, "This repeating element order: "+definition.getOrderMeaning(), null)); - } else { - // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null)); - } - } - 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 || !allowSubRows || definition.getFixed().isPrimitive()) { - String s = buildJson(definition.getFixed()); - String link = null; - if (Utilities.isAbsoluteUrl(s) && pkp != null) - link = pkp.getLinkForUrl(corePath, s); - 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); - } - if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { - Piece p = describeCoded(gen, definition.getFixed()); - if (p != null) - c.getPieces().add(p); - } - } 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 || !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"))); - genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly); - } - } else if (definition.hasExample()) { - for (ElementDefinitionExampleComponent ex : definition.getExample()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example")+("".equals("General")? "" : " "+ex.getLabel())+": ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); - } - } - if (definition.hasMaxLength() && definition.getMaxLength()!=0) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); - } - if (profile != null) { - for (StructureDefinitionMappingComponent md : profile.getMapping()) { - if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { - ElementDefinitionMappingComponent map = null; - for (ElementDefinitionMappingComponent m : definition.getMapping()) - if (m.getIdentity().equals(md.getIdentity())) - map = m; - if (map != null) { - for (int i = 0; i 0 || snapshot) { - ElementDefinition ed = findElementDefinition(sd, t.getName()); - if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) { - if (!skipnoValue) { - Row row = gen.new Row(); - erow.getSubRows().add(row); - Cell c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : corePath+(VersionUtilities.isR5Ver(context.getVersion()) ? "types-definitions.html#"+ed.getBase().getPath() : "element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); - c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece(null, null, null)); - c = gen.new Cell(); - row.getCells().add(c); - if (!pattern) { - c.addPiece(gen.new Piece(null, "0..0", null)); - row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); - } else if (isPrimitive(t.getTypeCode())) { - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); - } else if (isReference(t.getTypeCode())) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); - } else { - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); - } - c = gen.new Cell(); - row.getCells().add(c); - if (t.getTypeCode().contains("(")) { - String tc = t.getTypeCode(); - String tn = tc.substring(0, tc.indexOf("(")); - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, tn), tn, null)); - c.addPiece(gen.new Piece(null, "(", null)); - String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); - for (String s : p) { - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, s), s, null)); - } - c.addPiece(gen.new Piece(null, ")", null)); - } else { - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, t.getTypeCode()), t.getTypeCode(), null)); - } - c = gen.new Cell(); - c.addPiece(gen.new Piece(null, ed.getShort(), null)); - row.getCells().add(c); - } - } else { - for (Base b : t.getValues()) { - Row row = gen.new Row(); - erow.getSubRows().add(row); - row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); - - Cell c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : (VersionUtilities.isR5Ver(context.getVersion()) ? corePath+"types-definitions.html#"+ed.getBase().getPath() : corePath+"element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); - - c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece(null, null, null)); - - c = gen.new Cell(); - row.getCells().add(c); - if (pattern) - c.addPiece(gen.new Piece(null, "1.."+(t.getMaxCardinality() == 2147483647 ? "*" : Integer.toString(t.getMaxCardinality())), null)); - else - c.addPiece(gen.new Piece(null, "1..1", null)); - - c = gen.new Cell(); - row.getCells().add(c); - if (b.fhirType().contains("(")) { - String tc = b.fhirType(); - String tn = tc.substring(0, tc.indexOf("(")); - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, tn), tn, null)); - c.addPiece(gen.new Piece(null, "(", null)); - String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); - for (String s : p) { - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, s), s, null)); - } - c.addPiece(gen.new Piece(null, ")", null)); - } else { - c.addPiece(gen.new Piece(pkp.getLinkFor(corePath, b.fhirType()), b.fhirType(), null)); - } - - if (b.isPrimitive()) { - c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece(null, ed.getShort(), null)); - c.addPiece(gen.new Piece("br")); - c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); - String s = b.primitiveValue(); - // ok. let's see if we can find a relevant link for this - String link = null; - if (Utilities.isAbsoluteUrl(s)) { - link = pkp.getLinkForUrl(corePath, s); - } - c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen")); - } else { - c = gen.new Cell(); - row.getCells().add(c); - c.addPiece(gen.new Piece(null, ed.getShort(), null)); - c.addPiece(gen.new Piece("br")); - c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); - c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen")); - genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, skipnoValue); - } - } - } - } - } - } - - - private ElementDefinition findElementDefinition(StructureDefinition sd, String name) { - String path = sd.getTypeName()+"."+name; - for (ElementDefinition ed : sd.getSnapshot().getElement()) { - if (ed.getPath().equals(path)) - return ed; - } - throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT_, path)); - } - - - private String getFixedUrl(StructureDefinition sd) { - for (ElementDefinition ed : sd.getSnapshot().getElement()) { - if (ed.getPath().equals("Extension.url")) { - if (ed.hasFixed() && ed.getFixed() instanceof UriType) - return ed.getFixed().primitiveValue(); - } - } - return null; - } - - - private Piece describeCoded(HierarchicalTableGenerator gen, DataType fixed) { - if (fixed instanceof Coding) { - Coding c = (Coding) fixed; - ValidationResult vr = context.validateCode(terminologyServiceOptions , c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); - if (vr.getDisplay() != null) - return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); - } else if (fixed instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) fixed; - for (Coding c : cc.getCoding()) { - ValidationResult vr = context.validateCode(terminologyServiceOptions, c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); - if (vr.getDisplay() != null) - return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); - } - } - return null; - } - - - private boolean hasDescription(DataType fixed) { - if (fixed instanceof Coding) { - return ((Coding) fixed).hasDisplay(); - } else if (fixed instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) fixed; - if (cc.hasText()) - return true; - for (Coding c : cc.getCoding()) - if (c.hasDisplay()) - return true; - } // (fixed instanceof CodeType) || (fixed instanceof Quantity); - return false; - } - - - private boolean isCoded(DataType fixed) { - return (fixed instanceof Coding) || (fixed instanceof CodeableConcept) || (fixed instanceof CodeType) || (fixed instanceof Quantity); - } - - - private Cell generateGridDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, ElementDefinition valueDefn) throws IOException, FHIRException { - Cell c = gen.new Cell(); - row.getCells().add(c); - - if (used) { - if (definition.hasContentReference()) { - ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), definition.getContentReference(), profile); - if (ed == null) - c.getPieces().add(gen.new Piece(null, "Unknown reference to "+definition.getContentReference(), null)); - else { - if (ed.getSource() == profile) { - c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), "See "+ed.getElement().getPath(), null)); - } else { - c.getPieces().add(gen.new Piece(ed.getSource().getUserData("path")+"#"+ed.getElement().getPath(), "See "+ed.getSource().getTypeName()+"."+ed.getElement().getPath(), null)); - } - } - } - if (definition.getPath().endsWith("url") && definition.hasFixed()) { - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); - } else { - if (url != null) { - if (!c.getPieces().isEmpty()) - c.addPiece(gen.new Piece("br")); - String fullUrl = url.startsWith("#") ? baseURL+url : url; - StructureDefinition ed = context.fetchResource(StructureDefinition.class, url, profile); - String ref = null; - if (ed != null) { - String p = ed.getUserString("path"); - if (p != null) { - ref = p.startsWith("http:") || igmode ? p : Utilities.pathURL(corePath, p); - } - } - c.getPieces().add(gen.new Piece(null, "URL: ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(ref, fullUrl, null)); - } - - if (definition.hasSlicing()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); - } - if (definition != null) { - ElementDefinitionBindingComponent binding = null; - if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) - binding = valueDefn.getBinding(); - else if (definition.hasBinding()) - binding = definition.getBinding(); - if (binding!=null && !binding.isEmpty()) { - if (!c.getPieces().isEmpty()) - c.addPiece(gen.new Piece("br")); - BindingResolution br = pkp.resolveBinding(profile, binding, definition.getPath()); - c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, "Binding: ", null).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))); - if (binding.hasStrength()) { - c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null))); - c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null)); - } - if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { - c.getPieces().add(gen.new Piece(null, ": ", null)); - c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue()); - } - } - for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); - if (inv.getHumanElement().hasExtension(ToolingExtensions.EXT_REND_MD)) { - c.addMarkdown(inv.getHumanElement().getExtensionString(ToolingExtensions.EXT_REND_MD)); - } else { - c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); - } - } - if (definition.hasFixed()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold"))); - String s = buildJson(definition.getFixed()); - String link = null; - if (Utilities.isAbsoluteUrl(s)) - link = pkp.getLinkForUrl(corePath, s); - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); - } else if (definition.hasPattern()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); - } else if (definition.hasExample()) { - for (ElementDefinitionExampleComponent ex : definition.getExample()) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, "Example'"+("".equals("General")? "" : " "+ex.getLabel()+"'")+": ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); - } - } - if (definition.hasMaxLength() && definition.getMaxLength()!=0) { - if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); - } - if (profile != null) { - for (StructureDefinitionMappingComponent md : profile.getMapping()) { - if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { - ElementDefinitionMappingComponent map = null; - for (ElementDefinitionMappingComponent m : definition.getMapping()) - if (m.getIdentity().equals(md.getIdentity())) - map = m; - if (map != null) { - for (int i = 0; i list) { - CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); - for (ElementDefinitionSlicingDiscriminatorComponent id : list) - c.append((id.hasType() ? id.getType().toCode() : "??")+":"+id.getPath()); - return c.toString(); - } - - - private String describe(SlicingRules rules) { - if (rules == null) - return translate("sd.table", "Unspecified"); - switch (rules) { - case CLOSED : return translate("sd.table", "Closed"); - case OPEN : return translate("sd.table", "Open"); - case OPENATEND : return translate("sd.table", "Open At End"); - default: - return "?gen-sr?"; - } - } - - private boolean onlyInformationIsMapping(List list, ElementDefinition e) { - return (!e.hasSliceName() && !e.hasSlicing() && (onlyInformationIsMapping(e))) && - getChildren(list, e).isEmpty(); - } - - private boolean onlyInformationIsMapping(ElementDefinition d) { - return !d.hasShort() && !d.hasDefinition() && - !d.hasRequirements() && !d.getAlias().isEmpty() && !d.hasMinElement() && - !d.hasMax() && !d.getType().isEmpty() && !d.hasContentReference() && - !d.hasExample() && !d.hasFixed() && !d.hasMaxLengthElement() && - !d.getCondition().isEmpty() && !d.getConstraint().isEmpty() && !d.hasMustSupportElement() && - !d.hasBinding(); - } - - private boolean allAreReference(List types) { - for (TypeRefComponent t : types) { - if (!t.hasTarget()) - return false; - } - return true; - } - - private List getChildren(List all, ElementDefinition element) { - List result = new ArrayList(); - int i = all.indexOf(element)+1; - while (i < all.size() && all.get(i).getPath().length() > element.getPath().length()) { - if ((all.get(i).getPath().substring(0, element.getPath().length()+1).equals(element.getPath()+".")) && !all.get(i).getPath().substring(element.getPath().length()+1).contains(".")) - result.add(all.get(i)); - i++; - } - return result; - } - +// +// public static String describeExtensionContext(StructureDefinition ext) { +// StringBuilder b = new StringBuilder(); +// b.append("Use on "); +// for (int i = 0; i < ext.getContext().size(); i++) { +// StructureDefinitionContextComponent ec = ext.getContext().get(i); +// if (i > 0) +// b.append(i < ext.getContext().size() - 1 ? ", " : " or "); +// b.append(ec.getType().getDisplay()); +// b.append(" "); +// b.append(ec.getExpression()); +// } +// if (ext.hasContextInvariant()) { +// b.append(", with Context Invariant = "); +// boolean first = true; +// for (StringType s : ext.getContextInvariant()) { +// if (first) +// first = false; +// else +// b.append(", "); +// b.append(""+s.getValue()+""); +// } +// } +// return b.toString(); +// } + + + + +// public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, +// boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc) throws IOException, FHIRException { +// return generateTable(defFile, profile, diff, imageFolder, inlineGraphics, profileBaseFileName, snapshot, corePath, imagePath, logicalModel, allInvariants, outputTracker, mustSupport, rc, ""); +// } + + + + + protected String tail(String path) { if (path == null) { return ""; @@ -4725,7 +2795,7 @@ public class ProfileUtilities extends TranslatingUtilities { - public static class ElementDefinitionHolder { + private static class ElementDefinitionHolder { private String name; private ElementDefinition self; private int baseIndex = 0; @@ -4773,7 +2843,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } - public static class ElementDefinitionComparer implements Comparator { + private static class ElementDefinitionComparer implements Comparator { private boolean inExtension; private List snapshot; @@ -5265,7 +3335,7 @@ public class ProfileUtilities extends TranslatingUtilities { return false; } - public class SliceList { + private class SliceList { private Map slices = new HashMap<>(); @@ -5378,30 +3448,30 @@ public class ProfileUtilities extends TranslatingUtilities { private static String urlTail(String profile) { return profile.contains("/") ? profile.substring(profile.lastIndexOf("/")+1) : profile; } - - - private String checkName(String name) { -// if (name.contains(".")) -//// throw new Exception("Illegal name "+name+": no '.'"); -// if (name.contains(" ")) -// throw new Exception("Illegal name "+name+": no spaces"); - StringBuilder b = new StringBuilder(); - for (char c : name.toCharArray()) { - if (!Utilities.existsInList(c, '.', ' ', ':', '"', '\'', '(', ')', '&', '[', ']')) - b.append(c); - } - return b.toString().toLowerCase(); - } - - - private int charCount(String path, char t) { - int res = 0; - for (char ch : path.toCharArray()) { - if (ch == t) - res++; - } - return res; - } +// +// +// private String checkName(String name) { +//// if (name.contains(".")) +////// throw new Exception("Illegal name "+name+": no '.'"); +//// if (name.contains(" ")) +//// throw new Exception("Illegal name "+name+": no spaces"); +// StringBuilder b = new StringBuilder(); +// for (char c : name.toCharArray()) { +// if (!Utilities.existsInList(c, '.', ' ', ':', '"', '\'', '(', ')', '&', '[', ']')) +// b.append(c); +// } +// return b.toString().toLowerCase(); +// } +// +// +// private int charCount(String path, char t) { +// int res = 0; +// for (char ch : path.toCharArray()) { +// if (ch == t) +// res++; +// } +// return res; +// } // //private void generateForChild(TextStreamWriter txt, @@ -5635,271 +3705,6 @@ public class ProfileUtilities extends TranslatingUtilities { throw new Error("No slicing for "+slicer.getPath()); } - public class SpanEntry { - private List children = new ArrayList(); - private boolean profile; - private String id; - private String name; - private String resType; - private String cardinality; - private String description; - private String profileLink; - private String resLink; - private String type; - - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getResType() { - return resType; - } - public void setResType(String resType) { - this.resType = resType; - } - public String getCardinality() { - return cardinality; - } - public void setCardinality(String cardinality) { - this.cardinality = cardinality; - } - public String getDescription() { - return description; - } - public void setDescription(String description) { - this.description = description; - } - public String getProfileLink() { - return profileLink; - } - public void setProfileLink(String profileLink) { - this.profileLink = profileLink; - } - public String getResLink() { - return resLink; - } - public void setResLink(String resLink) { - this.resLink = resLink; - } - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public boolean isProfile() { - return profile; - } - public void setProfile(boolean profile) { - this.profile = profile; - } - public List getChildren() { - return children; - } - public String getType() { - return type; - } - public void setType(String type) { - this.type = type; - } - - } - - public XhtmlNode generateSpanningTable(StructureDefinition profile, String imageFolder, boolean onlyConstraints, String constraintPrefix, Set outputTracker) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, false, true); - gen.setTranslator(getTranslator()); - TableModel model = initSpanningTable(gen, "", false, profile.getId()); - Set processed = new HashSet(); - SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); - - genSpanEntry(gen, model.getRows(), span); - return gen.generate(model, "", 0, outputTracker); - } - - private SpanEntry buildSpanningTable(String name, String cardinality, StructureDefinition profile, Set processed, boolean onlyConstraints, String constraintPrefix) throws IOException { - SpanEntry res = buildSpanEntryFromProfile(name, cardinality, profile); - boolean wantProcess = !processed.contains(profile.getUrl()); - processed.add(profile.getUrl()); - if (wantProcess && profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { - for (ElementDefinition ed : profile.getSnapshot().getElement()) { - if (!"0".equals(ed.getMax()) && ed.getType().size() > 0) { - String card = getCardinality(ed, profile.getSnapshot().getElement()); - if (!card.endsWith(".0")) { - List refProfiles = listReferenceProfiles(ed); - if (refProfiles.size() > 0) { - String uri = refProfiles.get(0); - if (uri != null) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, uri); - if (sd != null && (!onlyConstraints || (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && (constraintPrefix == null || sd.getUrl().startsWith(constraintPrefix))))) { - res.getChildren().add(buildSpanningTable(nameForElement(ed), card, sd, processed, onlyConstraints, constraintPrefix)); - } - } - } - } - } - } - } - return res; - } - - - private String getCardinality(ElementDefinition ed, List list) { - int min = ed.getMin(); - int max = !ed.hasMax() || ed.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(ed.getMax()); - ElementDefinition ned = ed; - while (ned != null && ned.getPath().contains(".")) { - ned = findParent(ned, list); - if (ned != null) { // todo: this can happen if we've walked into a resoruce. Not sure what to about that? - if ("0".equals(ned.getMax())) - max = 0; - else if (!ned.getMax().equals("1") && !ned.hasSlicing()) - max = Integer.MAX_VALUE; - if (ned.getMin() == 0) { - min = 0; - } - } - } - return Integer.toString(min)+".."+(max == Integer.MAX_VALUE ? "*" : Integer.toString(max)); - } - - - private ElementDefinition findParent(ElementDefinition ed, List list) { - int i = list.indexOf(ed)-1; - while (i >= 0 && !ed.getPath().startsWith(list.get(i).getPath()+".")) - i--; - if (i == -1) - return null; - else - return list.get(i); - } - - - private List listReferenceProfiles(ElementDefinition ed) { - List res = new ArrayList(); - for (TypeRefComponent tr : ed.getType()) { - // code is null if we're dealing with "value" and profile is null if we just have Reference() - if (tr.hasTarget() && tr.hasTargetProfile()) - for (UriType u : tr.getTargetProfile()) - res.add(u.getValue()); - } - return res; - } - - - private String nameForElement(ElementDefinition ed) { - return ed.getPath().substring(ed.getPath().indexOf(".")+1); - } - - - private SpanEntry buildSpanEntryFromProfile(String name, String cardinality, StructureDefinition profile) throws IOException { - SpanEntry res = new SpanEntry(); - res.setName(name); - res.setCardinality(cardinality); - res.setProfileLink(profile.getUserString("path")); - res.setResType(profile.getTypeName()); - StructureDefinition base = context.fetchResource(StructureDefinition.class, res.getResType()); - if (base != null) - res.setResLink(base.getUserString("path")); - res.setId(profile.getId()); - res.setProfile(profile.getDerivation() == TypeDerivationRule.CONSTRAINT); - StringBuilder b = new StringBuilder(); - b.append(res.getResType()); - boolean first = true; - boolean open = false; - if (profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { - res.setDescription(profile.getName()); - for (ElementDefinition ed : profile.getSnapshot().getElement()) { - if (isKeyProperty(ed.getBase().getPath()) && ed.hasFixed()) { - if (first) { - open = true; - first = false; - b.append("["); - } else { - b.append(", "); - } - b.append(tail(ed.getBase().getPath())); - b.append("="); - b.append(summarize(ed.getFixed())); - } - } - if (open) - b.append("]"); - } else - res.setDescription("Base FHIR "+profile.getName()); - res.setType(b.toString()); - return res ; - } - - - private String summarize(DataType value) throws IOException { - if (value instanceof Coding) - return summarizeCoding((Coding) value); - else if (value instanceof CodeableConcept) - return summarizeCodeableConcept((CodeableConcept) value); - else - return buildJson(value); - } - - - private String summarizeCoding(Coding value) { - String uri = value.getSystem(); - String system = TerminologyRenderer.describeSystem(uri); - if (Utilities.isURL(system)) { - if (system.equals("http://cap.org/protocols")) - system = "CAP Code"; - } - return system+" "+value.getCode(); - } - - - private String summarizeCodeableConcept(CodeableConcept value) { - if (value.hasCoding()) - return summarizeCoding(value.getCodingFirstRep()); - else - return value.getText(); - } - - - private boolean isKeyProperty(String path) { - return Utilities.existsInList(path, "Observation.code"); - } - - - public TableModel initSpanningTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, String id) { - TableModel model = gen.new TableModel(id, true); - - model.setDocoImg(prefix+"help16.png"); - model.setDocoRef(Utilities.pathURL(prefix, "formats.html#table")); // todo: change to graph definition - model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Property", "A profiled resource", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Card.", "Minimum and Maximum # of times the the element can appear in the instance", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Content", "What goes here", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Description", "Description of the profile", null, 0)); - return model; - } - - private void genSpanEntry(HierarchicalTableGenerator gen, List rows, SpanEntry span) throws IOException { - Row row = gen.new Row(); - rows.add(row); - row.setAnchor(span.getId()); - //row.setColor(..?); - if (span.isProfile()) { - row.setIcon("icon_profile.png", HierarchicalTableGenerator.TEXT_ICON_PROFILE); - } else { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - } - - row.getCells().add(gen.new Cell(null, null, span.getName(), null, null)); - row.getCells().add(gen.new Cell(null, null, span.getCardinality(), null, null)); - row.getCells().add(gen.new Cell(null, span.getProfileLink(), span.getType(), null, null)); - row.getCells().add(gen.new Cell(null, null, span.getDescription(), null, null)); - - for (SpanEntry child : span.getChildren()) { - genSpanEntry(gen, row.getSubRows(), child); - } - } - public static ElementDefinitionSlicingDiscriminatorComponent interpretR2Discriminator(String discriminator, boolean isExists) { if (discriminator.endsWith("@pattern")) @@ -6073,7 +3878,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public List readChoices(ElementDefinition ed, List children) { + private List readChoices(ElementDefinition ed, List children) { List result = new ArrayList<>(); for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { ElementChoiceGroup grp = processConstraint(children, c); @@ -6084,7 +3889,7 @@ public class ProfileUtilities extends TranslatingUtilities { return result; } - private ElementChoiceGroup processConstraint(List children, ElementDefinitionConstraintComponent c) { + public ElementChoiceGroup processConstraint(List children, ElementDefinitionConstraintComponent c) { if (!c.hasExpression()) { return null; } @@ -6136,57 +3941,6 @@ public class ProfileUtilities extends TranslatingUtilities { return grp; } - public static boolean allTypesMustSupport(ElementDefinition e) { - boolean all = true; - boolean any = false; - for (TypeRefComponent tr : e.getType()) { - all = all && isMustSupport(tr); - any = any || isMustSupport(tr); - } - return !all && !any; - } - - public static boolean allProfilesMustSupport(List profiles) { - boolean all = true; - boolean any = false; - for (CanonicalType u : profiles) { - all = all && isMustSupport(u); - any = any || isMustSupport(u); - } - return !all && !any; - } - public static boolean isMustSupportDirect(TypeRefComponent tr) { - return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); - } - - public static boolean isMustSupport(TypeRefComponent tr) { - if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) { - return true; - } - if (isMustSupport(tr.getProfile())) { - return true; - } - return isMustSupport(tr.getTargetProfile()); - } - - public static boolean isMustSupport(List profiles) { - for (CanonicalType ct : profiles) { - if (isMustSupport(ct)) { - return true; - } - } - return false; - } - - - public static boolean isMustSupport(CanonicalType profile) { - return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT)); - } - - public ElementDefinitionResolution resolveContentRef(StructureDefinition structure, ElementDefinition element) { - return getElementById(structure, structure.getSnapshot().getElement(), element.getContentReference()); - } - public Set getMasterSourceFileNames() { return masterSourceFileNames; } @@ -6195,42 +3949,35 @@ public class ProfileUtilities extends TranslatingUtilities { this.masterSourceFileNames = masterSourceFileNames; } - public static ElementDefinitionConstraintComponent findConstraint(StructureDefinition sd, String inv) { - for (ElementDefinition ed : sd.getSnapshot().getElement()) { - for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { - if (c.getKey().equals(inv)) { - return c; - } - } - } - return null; + + public ProfileKnowledgeProvider getPkp() { + return pkp; } - public static String formatTypeSpecifiers(IWorkerContext context, ElementDefinition d) { - StringBuilder b = new StringBuilder(); - boolean first = true; - for (Extension e : d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { - if (first) first = false; else b.append("
"); - String cond = ToolingExtensions.readStringExtension(e, "condition"); - String type = ToolingExtensions.readStringExtension(e, "type"); - b.append("If "); - b.append(Utilities.escapeXml(cond)); - b.append(" then the type is "); - StructureDefinition sd = context.fetchTypeDefinition(type); - if (sd == null) { - b.append(""); - b.append(Utilities.escapeXml(type)); - b.append(""); - } else { - b.append(""); - b.append(Utilities.escapeXml(sd.getTypeName())); - b.append(""); - } - } - return b.toString(); - } + public static final String UD_ERROR_STATUS = "error-status"; + public static final int STATUS_OK = 0; + public static final int STATUS_HINT = 1; + public static final int STATUS_WARNING = 2; + public static final int STATUS_ERROR = 3; + public static final int STATUS_FATAL = 4; + private static final String ROW_COLOR_ERROR = "#ffcccc"; + private static final String ROW_COLOR_FATAL = "#ff9999"; + private static final String ROW_COLOR_WARNING = "#ffebcc"; + private static final String ROW_COLOR_HINT = "#ebf5ff"; + private static final String ROW_COLOR_NOT_MUST_SUPPORT = "#d6eaf8"; + + public String getRowColor(ElementDefinition element, boolean isConstraintMode) { + switch (element.getUserInt(UD_ERROR_STATUS)) { + case STATUS_HINT: return ROW_COLOR_HINT; + case STATUS_WARNING: return ROW_COLOR_WARNING; + case STATUS_ERROR: return ROW_COLOR_ERROR; + case STATUS_FATAL: return ROW_COLOR_FATAL; + } + if (isConstraintMode && !element.getMustSupport() && !element.getIsModifier() && element.getPath().contains(".")) + return null; // ROW_COLOR_NOT_MUST_SUPPORT; + else + return null; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java index c338d15a9..712e7d5ab 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java @@ -10,8 +10,9 @@ import java.util.Set; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ElementDefinition; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index 636bc80bd..810e5f513 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -15,7 +15,6 @@ import java.time.format.FormatStyle; import java.time.format.SignStyle; import java.util.Currency; import java.util.List; -import java.util.Set; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java index 499ac852b..296ed62fc 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java @@ -4,6 +4,7 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.utils.TranslatingUtilities; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; import org.hl7.fhir.utilities.validation.ValidationOptions; @@ -25,7 +26,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions; * @author graha * */ -public class Renderer { +public class Renderer extends TranslatingUtilities { protected RenderingContext context; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java index e290476d9..a37eea90d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java @@ -4,7 +4,6 @@ import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.utilities.Utilities; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RequirementsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RequirementsRenderer.java index 94900592f..af3376655 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RequirementsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RequirementsRenderer.java @@ -2,11 +2,9 @@ package org.hl7.fhir.r5.renderers; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.List; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.model.ActorDefinition; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalType; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index d5a6dee06..ecc4e9612 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -2,20 +2,80 @@ package org.hl7.fhir.r5.renderers; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.AdditionalBindingsRenderer; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementChoiceGroup; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ExtensionContext; +import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r5.formats.IParser; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataType; +import org.hl7.fhir.r5.model.Element; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; +import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionExampleComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; +import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; +import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; +import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.IntegerType; +import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.r5.model.Quantity; import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; +import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; +import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; +import org.hl7.fhir.r5.utils.PublicationHacker; +import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.MarkDownProcessor; +import org.hl7.fhir.utilities.StandardsStatus; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; +import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; +import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; public class StructureDefinitionRenderer extends ResourceRenderer { + private List keyRows = new ArrayList<>(); + public StructureDefinitionRenderer(RenderingContext context) { super(context); } @@ -29,7 +89,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException { - x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, + x.getChildNodes().add(generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getLink(KnownLinkType.SPEC), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, context, "")); return true; } @@ -57,4 +117,2166 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return "??"; } + +// private static final int AGG_NONE = 0; +// private static final int AGG_IND = 1; +// private static final int AGG_GR = 2; +// private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; + private static final String CONSTRAINT_CHAR = "C"; + private static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; + private final boolean ADD_REFERENCE_TO_TABLE = true; + + private boolean useTableForFixedValues = true; + + public static class UnusedTracker { + private boolean used; + } + + private class SpanEntry { + private List children = new ArrayList(); + private boolean profile; + private String id; + private String name; + private String resType; + private String cardinality; + private String description; + private String profileLink; + private String resLink; + private String type; + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getResType() { + return resType; + } + public void setResType(String resType) { + this.resType = resType; + } + public String getCardinality() { + return cardinality; + } + public void setCardinality(String cardinality) { + this.cardinality = cardinality; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public String getProfileLink() { + return profileLink; + } + public void setProfileLink(String profileLink) { + this.profileLink = profileLink; + } + public String getResLink() { + return resLink; + } + public void setResLink(String resLink) { + this.resLink = resLink; + } + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + public boolean isProfile() { + return profile; + } + public void setProfile(boolean profile) { + this.profile = profile; + } + public List getChildren() { + return children; + } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + + } + + private class ElementInStructure { + + private StructureDefinition source; + private ElementDefinition element; + + public ElementInStructure(StructureDefinition source, ElementDefinition ed) { + this.source = source; + this.element = ed; + } + + public StructureDefinition getSource() { + return source; + } + + public ElementDefinition getElement() { + return element; + } + + } + private ElementInStructure getElementByName(List elements, String contentReference, StructureDefinition source) { + if (contentReference.contains("#")) { + String url = contentReference.substring(0, contentReference.indexOf("#")); + contentReference = contentReference.substring(contentReference.indexOf("#")); + if (Utilities.noString(url)) { + url = source.getUrl(); + } + if (!url.equals(source.getUrl())) { + source = context.getWorker().fetchResource(StructureDefinition.class, url, source); + if (source == null) { + throw new FHIRException("Unable to resolve StructureDefinition "+url+" resolving content reference "+contentReference); + } + elements = source.getSnapshot().getElement(); + } + } + for (ElementDefinition ed : elements) { + if (("#"+ed.getPath()).equals(contentReference)) { + return new ElementInStructure(source, ed); + } + if (("#"+ed.getId()).equals(contentReference)) { + return new ElementInStructure(source, ed); + } + } + throw new Error("getElementByName: can't find "+contentReference+" in "+elements.toString()+" from "+source.getUrl()); +// return null; + } + + public XhtmlNode generateGrid(String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, String corePath, String imagePath, Set outputTracker) throws IOException, FHIRException { + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); + gen.setTranslator(getTranslator()); + TableModel model = gen.initGridTable(corePath, profile.getId()); + List list = profile.getSnapshot().getElement(); + List profiles = new ArrayList(); + profiles.add(profile); + genGridElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, true, profileBaseFileName, null, corePath, imagePath, true, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list)); + try { + return gen.generate(model, imagePath, 1, outputTracker); + } catch (org.hl7.fhir.exceptions.FHIRException e) { + throw new FHIRException(e.getMessage(), e); + } + } + + + + public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, + boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException { + assert(diff != snapshot);// check it's ok to get rid of one of these + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); + gen.setTranslator(getTranslator()); + TableModel model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER); + List list; + if (diff) + list = supplementMissingDiffElements(profile); + else { + list = new ArrayList<>(); + list.addAll(profile.getSnapshot().getElement()); + } + List profiles = new ArrayList(); + profiles.add(profile); + keyRows.clear(); + + genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile); + try { + return gen.generate(model, imagePath, 0, outputTracker); + } catch (org.hl7.fhir.exceptions.FHIRException e) { + throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); + } + } + + private Row genElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, + boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, Resource srcSD) throws IOException, FHIRException { + Row originalRow = slicingRow; + StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); + Row typesRow = null; + + List children = getChildren(all, element); +// if (!snapshot && isExtension && extensions != null && extensions != isExtension) +// return; + + if (!onlyInformationIsMapping(all, element)) { + Row row = gen.new Row(); + row.setAnchor(element.getPath()); + row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); + if (element.hasSlicing()) + row.setLineColor(1); + else if (element.hasSliceName()) + row.setLineColor(2); + else + row.setLineColor(0); + boolean hasDef = element != null; + boolean ext = false; + if (tail(element.getPath()).equals("extension")) { + if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) + row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + else + row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + ext = true; + } else if (tail(element.getPath()).equals("modifierExtension")) { + if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) + row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + else + row.setIcon("icon_modifier_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + } else if (!hasDef || element.getType().size() == 0) { + if (root && profile != null && context.getWorker().getResourceNames().contains(profile.getType())) { + row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { + row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); + keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); + } else { + row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + } + } else if (hasDef && element.getType().size() > 1) { + if (allAreReference(element.getType())) { + row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + } else if (element.hasExtension(ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { + row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + } else { + row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + typesRow = row; + } + } else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { + row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); + } else if (hasDef && isPrimitive(element.getType().get(0).getWorkingCode())) { + if (keyRows.contains(element.getId())) { + row.setIcon("icon-key.png", HierarchicalTableGenerator.TEXT_ICON_KEY); + } else { + row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + } + } else if (hasDef && element.getType().get(0).hasTarget()) { + row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + } else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) { + row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { + row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); + keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); + } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { + row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + } else { + row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + } + if (element.hasUserData("render.opaque")) { + row.setOpacity("0.5"); + } + UnusedTracker used = new UnusedTracker(); + String ref = defPath == null ? null : defPath + anchorPrefix + element.getId(); + String sName = tail(element.getPath()); + if (element.hasSliceName()) + sName = sName +":"+element.getSliceName(); + used.used = true; + 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, all); + genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc); + 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(); + showMissing = false; //? + } else { + row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); + slicingRow = row; + for (Cell cell : row.getCells()) + for (Piece p : cell.getPieces()) { + p.addStyle("font-style: italic"); + } + } + } else if (element.hasSliceName()) { + row.setIcon("icon_slice_item.png", HierarchicalTableGenerator.TEXT_ICON_SLICE_ITEM); + } + if (used.used || showMissing) + rows.add(row); + if (!used.used && !element.hasSlicing()) { + for (Cell cell : row.getCells()) + for (Piece p : cell.getPieces()) { + p.setStyle("text-decoration:line-through"); + p.setReference(null); + } + } else { + if (slicingRow != originalRow && !children.isEmpty()) { + // we've entered a slice; we're going to create a holder row for the slice children + Row hrow = gen.new Row(); + hrow.setAnchor(element.getPath()); + hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); + hrow.setLineColor(1); + hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + hrow.getCells().add(gen.new Cell(null, null, sName+":All Slices", "", null)); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all slices", "", null)); + row.getSubRows().add(hrow); + row = hrow; + } + if (typesRow != null && !children.isEmpty()) { + // we've entered a typing slice; we're going to create a holder row for the all types children + Row hrow = gen.new Row(); + hrow.setAnchor(element.getPath()); + hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); + hrow.setLineColor(1); + hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + hrow.getCells().add(gen.new Cell(null, null, sName+":All Types", "", null)); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all Types", "", null)); + row.getSubRows().add(hrow); + row = hrow; + } + + Row currRow = row; + List groups = readChoices(element, children); + boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension"); + if (!element.prohibited()) { + for (ElementDefinition child : children) { + if (!child.hasSliceName()) { + currRow = row; + } + Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); + + if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { + currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix, srcSD); + } + } + } +// if (!snapshot && (extensions == null || !extensions)) +// for (ElementDefinition child : children) +// if (child.getPath().endsWith(".extension") || child.getPath().endsWith(".modifierExtension")) +// genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); + } + if (typesRow != null && !element.prohibited()) { + makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport, srcSD); + } + } + return slicingRow; + } + + public Cell genElementNameCell(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, List elements) throws IOException { + String hint = ""; + hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : "")); + if (hasDef && element.hasDefinition()) { + hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); + hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); + } + if (element.hasSlicing() && slicesExist(elements, element)) { // some elements set up slicing but don't actually slice, so we don't augment the name + sName = "Slices for "+sName; + } + Cell left = gen.new Cell(null, ref, sName, hint, null); + row.getCells().add(left); + return left; + } + + 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, boolean allowSubRows, RenderingContext rc) throws IOException { + List res = new ArrayList<>(); + Cell gc = gen.new Cell(); + row.getCells().add(gc); + res.add(gc); + if (element != null && element.getIsModifier()) { + checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false)); + } + if (element != null && element.getMustSupport()) { + checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false)); + } + if (element != null && element.getIsSummary()) { + checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); + } + if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { + Piece p = gc.addText(CONSTRAINT_CHAR); + p.setHint(translate("sd.table", "This element has or is affected by constraints ("+listConstraintsAndConditions(element)+")")); + p.addStyle(CONSTRAINT_STYLE); + p.setReference(Utilities.pathURL(VersionUtilities.getSpecUrl(context.getWorker().getVersion()), "conformance-rules.html#constraints")); + } + if (element != null && element.hasExtension(ToolingExtensions.EXT_STANDARDS_STATUS)) { + StandardsStatus ss = StandardsStatus.fromCode(element.getExtensionString(ToolingExtensions.EXT_STANDARDS_STATUS)); + gc.addStyledText("Standards Status = "+ss.toDisplay(), ss.getAbbrev(), "black", ss.getColor(), context.getWorker().getSpecUrl()+"versions.html#std-process", true); + } + + ExtensionContext extDefn = null; + if (ext) { + if (element != null) { + if (element.getType().size() == 1 && element.getType().get(0).hasProfile()) { + String eurl = element.getType().get(0).getProfile().get(0).getValue(); + extDefn = locateExtension(StructureDefinition.class, eurl); + 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(ProfileUtilities.UD_DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + } else { + String name = element.hasSliceName() ? element.getSliceName() : urltail(eurl); + nameCell.getPieces().get(0).setText(name); + // left.getPieces().get(0).setReference((String) extDefn.getExtensionStructure().getTag("filename")); + nameCell.getPieces().get(0).setHint(translate("sd.table", "Extension URL")+" = "+extDefn.getUrl()); + res.add(genCardinality(gen, element, row, hasDef, used, extDefn.getElement())); + ElementDefinition valueDefn = extDefn.getExtensionValueDefinition(); + if (valueDefn != null && !"0".equals(valueDefn.getMax())) + res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); + 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, allowSubRows, rc)); + } + } else { + res.add(genCardinality(gen, element, row, hasDef, used, null)); + if ("0".equals(element.getMax())) + 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(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + } + } + } else if (element != null) { + res.add(genCardinality(gen, element, row, hasDef, used, null)); + if (hasDef && !"0".equals(element.getMax()) && typesRow == null) + 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(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + } + return res; + } + + private Cell genCardinality(HierarchicalTableGenerator gen, ElementDefinition definition, Row row, boolean hasDef, UnusedTracker tracker, ElementDefinition fallback) { + IntegerType min = !hasDef ? new IntegerType() : definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); + StringType max = !hasDef ? new StringType() : definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); + if (min.isEmpty() && definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER) != null) { + ElementDefinition base = (ElementDefinition) definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); + if (base.hasMinElement()) { + min = base.getMinElement().copy(); + min.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); + } + } + if (max.isEmpty() && definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER) != null) { + ElementDefinition base = (ElementDefinition) definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); + if (base.hasMaxElement()) { + max = base.getMaxElement().copy(); + max.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); + } + } + if (min.isEmpty() && fallback != null) + min = fallback.getMinElement(); + if (max.isEmpty() && fallback != null) + max = fallback.getMaxElement(); + + if (!max.isEmpty()) + tracker.used = !max.getValue().equals("0"); + + String hint = null; + if (max.hasValue() && min.hasValue() && "*".equals(max.getValue()) && 0 == min.getValue()) { + if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { + String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); + if ("present".equals(code)) { + hint = "This element is present as a JSON Array even when there are no items in the instance"; + } else { + hint = "This element may be present as a JSON Array even when there are no items in the instance"; + } + } + } + Cell cell = gen.new Cell(null, null, null, null, null); + row.getCells().add(cell); + if (!min.isEmpty() || !max.isEmpty()) { + cell.addPiece(checkForNoChange(min, gen.new Piece(null, !min.hasValue() ? "" : Integer.toString(min.getValue()), hint))); + cell.addPiece(checkForNoChange(min, max, gen.new Piece(null, "..", hint))); + cell.addPiece(checkForNoChange(max, gen.new Piece(null, !max.hasValue() ? "" : max.getValue(), hint))); + } + return cell; + } + + private List supplementMissingDiffElements(StructureDefinition profile) { + List list = new ArrayList<>(); + list.addAll(profile.getDifferential().getElement()); + if (list.isEmpty()) { + ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); + root.setId(profile.getTypeName()); + list.add(root); + } else { + if (list.get(0).getPath().contains(".")) { + ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); + root.setId(profile.getTypeName()); + list.add(0, root); + } + } + insertMissingSparseElements(list); + return list; + } + + private boolean usesMustSupport(List list) { + for (ElementDefinition ed : list) + if (ed.hasMustSupport() && ed.getMustSupport()) + return true; + return false; + } + + + + private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) { + String name = tail(element.getPath()); + for (ElementChoiceGroup grp : groups) { + if (grp.getElements().contains(name)) { + if (grp.getRow() == null) { + grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode)); + } + return grp.getRow(); + } + } + return row; + } + + private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) { + Row row = gen.new Row(); + row.setAnchor(parent.getPath()+"-"+grp.getName()); + row.setColor(context.getProfileUtilities().getRowColor(parent, isConstraintMode)); + row.setLineColor(1); + row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.getCells().add(gen.new Cell(null, null, "(Choice of one)", "", null)); + row.getCells().add(gen.new Cell()); + row.getCells().add(gen.new Cell(null, null, (grp.isMandatory() ? "1" : "0")+"..1", "", null)); + row.getCells().add(gen.new Cell()); + row.getCells().add(gen.new Cell()); + prow.getSubRows().add(row); + return row; + } + + + private void insertMissingSparseElements(List list) { + int i = 1; + while (i < list.size()) { + String[] pathCurrent = list.get(i).getPath().split("\\."); + String[] pathLast = list.get(i-1).getPath().split("\\."); + int firstDiff = 0; // the first entry must be a match + while (firstDiff < pathCurrent.length && firstDiff < pathLast.length && pathCurrent[firstDiff].equals(pathLast[firstDiff])) { + firstDiff++; + } + if (!(isSibling(pathCurrent, pathLast, firstDiff) || isChild(pathCurrent, pathLast, firstDiff))) { + // now work backwards down to lastMatch inserting missing path nodes + ElementDefinition parent = findParent(list, i, list.get(i).getPath()); + int parentDepth = Utilities.charCount(parent.getPath(), '.')+1; + int childDepth = Utilities.charCount(list.get(i).getPath(), '.')+1; + if (childDepth > parentDepth + 1) { + String basePath = parent.getPath(); + String baseId = parent.getId(); + for (int index = parentDepth; index >= firstDiff; index--) { + String mtail = makeTail(pathCurrent, parentDepth, index); + ElementDefinition root = new ElementDefinition().setPath(basePath+"."+mtail); + root.setId(baseId+"."+mtail); + list.add(i, root); + } + } + } + i++; + } + } + + + private String urltail(String path) { + if (path.contains("#")) + return path.substring(path.lastIndexOf('#')+1); + if (path.contains("/")) + return path.substring(path.lastIndexOf('/')+1); + else + return path; + + } + + private boolean standardExtensionSlicing(ElementDefinition element) { + String t = tail(element.getPath()); + return (t.equals("extension") || t.equals("modifierExtension")) + && 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, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { + return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc); + } + + 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, RenderingContext rc) throws IOException, FHIRException { + Cell c = gen.new Cell(); + row.getCells().add(c); + + if (used) { + if (logicalModel && ToolingExtensions.hasExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) { + if (root) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); + } else if (!root && ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace") && + !ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace").equals(ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); + } + } + if (root) { + 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.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type")+". ", null)); + + List children = new ArrayList<>(); + for (StructureDefinition sd : context.getWorker().fetchResourcesByType(StructureDefinition.class)) { + if (sd.hasBaseDefinition() && sd.getBaseDefinition().equals(profile.getUrl())) { + children.add(sd); + } + } + if (!children.isEmpty()) { + c.addPiece(gen.new Piece(null, "Child "+(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types")+": ", null)); + boolean first = true; + for (StructureDefinition sd : children) { + if (first) first = false; else c.addPiece(gen.new Piece(null, ", ", null)); + c.addPiece(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); + } + } + } + } + if (definition.getPath().endsWith("url") && definition.hasFixed()) { + c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); + } else { + if (definition != null && definition.hasShort()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null))); + } else if (fallback != null && fallback.hasShort()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5")); + } + if (url != null) { + if (!c.getPieces().isEmpty()) + c.addPiece(gen.new Piece("br")); + String fullUrl = url.startsWith("#") ? baseURL+url : url; + StructureDefinition ed = context.getWorker().fetchResource(StructureDefinition.class, url, profile); + String ref = null; + String ref2 = null; + String fixedUrl = null; + if (ed != null) { + String p = ed.getUserString("path"); + if (p != null) { + ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p); + } + fixedUrl = getFixedUrl(ed); + if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension? + if (fixedUrl.equals(url)) + fixedUrl = null; + else { + StructureDefinition ed2 = context.getWorker().fetchResource(StructureDefinition.class, fixedUrl); + if (ed2 != null) { + String p2 = ed2.getUserString("path"); + if (p2 != null) { + ref2 = p2.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p2 : Utilities.pathURL(corePath, p2); + } + } + } + } + } + if (fixedUrl == null) { + if (!Utilities.noString(fullUrl)) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(ref, fullUrl, null)); + } + } else { + // reference to a profile take on the extension show the base URL + c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(ref2, fixedUrl, null)); + c.getPieces().add(gen.new Piece(null, translate("sd.table", " profiled by ")+" ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(ref, fullUrl, null)); + + } + } + + if (definition.hasSlicing()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); + } + if (!definition.getPath().contains(".") && ToolingExtensions.hasExtension(profile, ToolingExtensions.EXT_BINDING_STYLE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null)); + c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null)); + c.getPieces().add(gen.new Piece(null, " binding style", null)); + + } + if (definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "When this element is read ", null)); + Piece piece = gen.new Piece("code"); + piece.addHtml(new XhtmlNode(NodeType.Text).setContent(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_IMPLIED_PREFIX))); + c.getPieces().add(piece); + c.getPieces().add(gen.new Piece(null, " is prefixed to the value before validation", null)); + } + + if (definition.hasExtension(ToolingExtensions.EXT_EXTENSION_STYLE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String es = definition.getExtensionString(ToolingExtensions.EXT_EXTENSION_STYLE); + if ("named-elements".equals(es)) { + if (rc.hasLink(KnownLinkType.JSON_NAMES)) { + c.getPieces().add(gen.new Piece(rc.getLink(KnownLinkType.JSON_NAMES), "This element can be extended by named JSON elements", null)); + } else { + c.getPieces().add(gen.new Piece(ToolingExtensions.WEB_EXTENSION_STYLE, "This element can be extended by named JSON elements", null)); + } + } + } + if (definition.hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) { + String ide = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_ID_EXPECTATION); + if (ide.equals("optional")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "Id may or not be present (this is the default for elements but not resources)", null)); + } else if (ide.equals("required")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "Id is required to be present (this is the default for resources but not elements)", null)); + } else if (ide.equals("required")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "An ID is not allowed in this context", null)); + } + } + if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); + c.getPieces().add(gen.new Piece(null, " (", null)); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); + c.getPieces().add(gen.new Piece(null, ")", null)); + } else { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Element Name")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); + } + } else if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); + } + if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); + if ("present".equals(code)) { + c.getPieces().add(gen.new Piece(null, "JSON: This element is present as a JSON Array even when there are no items in the instance", null)); + } else { + c.getPieces().add(gen.new Piece(null, "JSON: This element may be present as a JSON Array even when there are no items in the instance", null)); + } + } + String jn = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_NAME); + if (!Utilities.noString(jn)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + if (definition.getPath().contains(".")) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, jn, null)); + } else { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name for Type")+": ", null).addStyle("font-weight:bold")); + Piece piece = gen.new Piece("code"); + piece.addHtml(new XhtmlNode(NodeType.Text).setContent(jn)); + c.getPieces().add(piece); + } + } + + if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "JSON: The type of this element is inferred from the JSON type in the instance", null)); + } + if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "JSON: This object can be represented as null in the JSON structure (which counts as 'present' for cardinality purposes)", null)); + } + if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); + c.getPieces().add(gen.new Piece(null, "JSON: Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null)); + } + if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { + for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String cond = ToolingExtensions.readStringExtension(e, "condition"); + String type = ToolingExtensions.readStringExtension(e, "type"); + c.getPieces().add(gen.new Piece(null, "JSON: If ", null)); + Piece piece = gen.new Piece("code"); + piece.addHtml(new XhtmlNode(NodeType.Text).setContent(cond)); + c.getPieces().add(piece); + c.getPieces().add(gen.new Piece(null, "then the type is ", null)); + StructureDefinition sd = context.getWorker().fetchTypeDefinition(type); + if (sd == null) { + c.getPieces().add(gen.new Piece("")); + c.getPieces().add(gen.new Piece(null, type, null)); + c.getPieces().add(gen.new Piece("")); + } else { + c.getPieces().add(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); + } + } + } + if (definition != null) { + ElementDefinitionBindingComponent binding = null; + if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) + binding = makeUnifiedBinding(valueDefn.getBinding(), valueDefn); + else if (definition.hasBinding()) + binding = makeUnifiedBinding(definition.getBinding(), definition); + if (binding!=null && !binding.isEmpty()) { + if (!c.getPieces().isEmpty()) + c.addPiece(gen.new Piece("br")); + BindingResolution br = context.getPkp() == null ? makeNullBr(binding) : context.getPkp().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) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null))); + if (binding.hasStrength()) { + c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, " (", null))); + c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition()))); + c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null))); + } + if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { + c.getPieces().add(gen.new Piece(null, ": ", null)); + c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()))); + } + + AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, profile, definition.getPath(), rc, null); + if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { + abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)); + } + if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) { + abr.seeMinBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MIN_VALUESET)); + } + if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { + abr.seeAdditionalBindings(binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)); + } + abr.render(gen, c); + } + for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { + if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { + if (!c.getPieces().isEmpty()) + c.addPiece(gen.new Piece("br")); + 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))); + } + } + if ((definition.hasBase() && "*".equals(definition.getBase().getMax())) || (definition.hasMax() && "*".equals(definition.getMax()))) { + if (c.getPieces().size() > 0) + c.addPiece(gen.new Piece("br")); + if (definition.hasOrderMeaning()) { + c.getPieces().add(gen.new Piece(null, "This repeating element order: "+definition.getOrderMeaning(), null)); + } else { + // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null)); + } + } + 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 || !allowSubRows || definition.getFixed().isPrimitive()) { + String s = buildJson(definition.getFixed()); + String link = null; + if (Utilities.isAbsoluteUrl(s) && context.getPkp() != null) + link = context.getPkp().getLinkForUrl(corePath, s); + 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); + } + if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { + Piece p = describeCoded(gen, definition.getFixed()); + if (p != null) + c.getPieces().add(p); + } + } 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 || !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"))); + genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly); + } + } else if (definition.hasExample()) { + for (ElementDefinitionExampleComponent ex : definition.getExample()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example")+("".equals("General")? "" : " "+ex.getLabel())+": ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); + } + } + if (definition.hasMaxLength() && definition.getMaxLength()!=0) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); + } + if (profile != null) { + for (StructureDefinitionMappingComponent md : profile.getMapping()) { + if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { + ElementDefinitionMappingComponent map = null; + for (ElementDefinitionMappingComponent m : definition.getMapping()) + if (m.getIdentity().equals(md.getIdentity())) + map = m; + if (map != null) { + for (int i = 0; i types = e.getType(); + if (!e.hasType()) { + if (root) { // we'll use base instead of types then + StructureDefinition bsd = profile == null ? null : context.getWorker().fetchResource(StructureDefinition.class, profile.getBaseDefinition(), profile); + 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)); + } else { + c.getPieces().add(gen.new Piece(null, bsd.getName(), null)); + } + } + return c; + } else if (e.hasContentReference()) { + return c; + } else { + ElementDefinition d = (ElementDefinition) e.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); + if (d != null && d.hasType()) { + types = new ArrayList(); + for (TypeRefComponent tr : d.getType()) { + TypeRefComponent tt = tr.copy(); + tt.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); + types.add(tt); + } + } else { + return c; + } + } + } + + boolean first = true; + + TypeRefComponent tl = null; + for (TypeRefComponent t : types) { + if (!mustSupportMode || allTypesMustSupport(e) || isMustSupport(t)) { + if (first) { + first = false; + } else { + c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); + } + tl = t; + if (t.hasTarget()) { + c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); + if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + c.getPieces().add(gen.new Piece(null, "(", null)); + boolean tfirst = true; + for (CanonicalType u : t.getTargetProfile()) { + if (!mustSupportMode || allProfilesMustSupport(t.getTargetProfile()) || isMustSupport(u)) { + if (tfirst) + tfirst = false; + else + c.addPiece(gen.new Piece(null, " | ", null)); + genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue(), null); + if (!mustSupportMode && isMustSupport(u) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } + } + } + c.getPieces().add(gen.new Piece(null, ")", null)); + if (t.getAggregation().size() > 0) { + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); + boolean firstA = true; + for (Enumeration a : t.getAggregation()) { + if (firstA == true) + firstA = false; + else + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); + } + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); + } + } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type + String ref; + boolean pfirst = true; + for (CanonicalType p : t.getProfile()) { + if (!mustSupportMode || allProfilesMustSupport(t.getProfile()) || isMustSupport(p)) { + if (pfirst) { + pfirst = false; + } else { + c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); + } + + ref = context.getPkp() == null ? null : context.getPkp().getLinkForProfile(profile, p.getValue()); + if (ref != null) { + String[] parts = ref.split("\\|"); + if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { + if (p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) { + String pp = p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT); + pp = pp.substring(pp.indexOf(".")); + c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1]+pp, t.getWorkingCode()))); + } else { + c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); + } + } else { + c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); + } + } else { + c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); + } + if (!mustSupportMode && isMustSupport(p) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } + } + } + } else { + String tc = t.getWorkingCode(); + if (Utilities.isAbsoluteUrl(tc)) { + StructureDefinition sd = context.getWorker().fetchTypeDefinition(tc); + if (sd == null) { + c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); + } else { + c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), sd.getTypeName(), null))); + } + } else if (context.getPkp() != null && context.getPkp().hasLinkFor(tc)) { + c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); + } else { + c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); + } + if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + } + } + } + return c; + } + + + private String pfx(String prefix, String url) { + return Utilities.isAbsoluteUrl(url) ? url : prefix + url; + } + + private void genTargetLink(HierarchicalTableGenerator gen, String profileBaseFileName, String corePath, Cell c, TypeRefComponent t, String u, Resource src) { + if (u.startsWith("http://hl7.org/fhir/StructureDefinition/")) { + StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, u, src); + if (sd != null) { + String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); + c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getUserString("path")), disp, null))); + } else { + String rn = u.substring(40); + c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, rn), rn, null))); + } + } else if (Utilities.isAbsoluteUrl(u)) { + StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, u, src); + if (sd != null && context.getPkp() != null) { + String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); + String ref = context.getPkp().getLinkForProfile(null, sd.getUrl()); + if (ref != null && ref.contains("|")) + ref = ref.substring(0, ref.indexOf("|")); + c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null))); + } else + c.addPiece(checkForNoChange(t, gen.new Piece(null, u, null))); + } else if (t.hasTargetProfile() && u.startsWith("#")) + c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.substring(1).toLowerCase()+".html", u, null))); + } + + private boolean isProfiledType(List theProfile) { + for (CanonicalType next : theProfile){ + if (StringUtils.defaultString(next.getValueAsString()).contains(":")) { + return true; + } + } + return false; + } + + + private static String codeForAggregation(AggregationMode a) { + switch (a) { + case BUNDLED : return "b"; + case CONTAINED : return "c"; + case REFERENCED: return "r"; + default: return "?"; + } + } + + private static String hintForAggregation(AggregationMode a) { + if (a != null) + return a.getDefinition(); + else + return null; + } + + + private String checkPrepend(String corePath, String path) { + if (context.getPkp() != null && context.getPkp().prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) + return corePath+path; + else + return path; + } + + + private ElementDefinition findParent(List list, int i, String path) { + while (i > 0 && !path.startsWith(list.get(i).getPath()+".")) { + i--; + } + return list.get(i); + } + + private boolean isSibling(String[] pathCurrent, String[] pathLast, int firstDiff) { + return pathCurrent.length == pathLast.length && firstDiff == pathCurrent.length-1; + } + + + private boolean isChild(String[] pathCurrent, String[] pathLast, int firstDiff) { + return pathCurrent.length == pathLast.length+1 && firstDiff == pathLast.length; + } + + private String makeTail(String[] pathCurrent, int start, int index) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("."); + for (int i = start; i <= index; i++) { + b.append(pathCurrent[i]); + } + return b.toString(); + } + + private void genGridElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, String corePath, String imagePath, boolean root, boolean isConstraintMode) throws IOException, FHIRException { + StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); + String s = tail(element.getPath()); + List children = getChildren(all, element); + boolean isExtension = (s.equals("extension") || s.equals("modifierExtension")); + + if (!onlyInformationIsMapping(all, element)) { + Row row = gen.new Row(); + row.setAnchor(element.getPath()); + row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); + if (element.hasSlicing()) + row.setLineColor(1); + else if (element.hasSliceName()) + row.setLineColor(2); + else + row.setLineColor(0); + boolean hasDef = element != null; + String ref = defPath == null ? null : defPath + element.getId(); + UnusedTracker used = new UnusedTracker(); + used.used = true; + Cell left = gen.new Cell(); + if (element.getType().size() == 1 && element.getType().get(0).isPrimitive()) + left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement())).addStyle("font-weight:bold")); + else + left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement()))); + if (element.hasSliceName()) { + left.getPieces().add(gen.new Piece("br")); + String indent = StringUtils.repeat('\u00A0', 1+2*(element.getPath().split("\\.").length)); + left.getPieces().add(gen.new Piece(null, indent + "("+element.getSliceName() + ")", null)); + } + row.getCells().add(left); + + genCardinality(gen, element, row, hasDef, used, null); + if (hasDef && !"0".equals(element.getMax())) + genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, false); + else + row.getCells().add(gen.new Cell()); + generateGridDescription(gen, row, element, null, used.used, null, null, profile, corePath, imagePath, root, null); +/* if (element.hasSlicing()) { + if (standardExtensionSlicing(element)) { + used.used = element.hasType() && element.getType().get(0).hasProfile(); + showMissing = false; + } else { + row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); + row.getCells().get(2).getPieces().clear(); + for (Cell cell : row.getCells()) + for (Piece p : cell.getPieces()) { + p.addStyle("font-style: italic"); + } + } + }*/ + rows.add(row); + for (ElementDefinition child : children) + if (child.getMustSupport()) + genGridElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, corePath, imagePath, false, isConstraintMode); + } + } + + + private ExtensionContext locateExtension(Class class1, String value) { + if (value.contains("#")) { + StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); + if (ext == null) + return null; + String tail = value.substring(value.indexOf("#")+1); + ElementDefinition ed = null; + for (ElementDefinition ted : ext.getSnapshot().getElement()) { + if (tail.equals(ted.getSliceName())) { + ed = ted; + return new ExtensionContext(ext, ed); + } + } + return null; + } else { + StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value); + if (ext == null) + return null; + else + return new ExtensionContext(ext, ext.getSnapshot().getElement().get(0)); + } + } + + + private boolean extensionIsComplex(String value) { + if (value.contains("#")) { + StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); + if (ext == null) + return false; + String tail = value.substring(value.indexOf("#")+1); + ElementDefinition ed = null; + for (ElementDefinition ted : ext.getSnapshot().getElement()) { + if (tail.equals(ted.getSliceName())) { + ed = ted; + break; + } + } + if (ed == null) + return false; + int i = ext.getSnapshot().getElement().indexOf(ed); + int j = i+1; + while (j < ext.getSnapshot().getElement().size() && !ext.getSnapshot().getElement().get(j).getPath().equals(ed.getPath())) + j++; + return j - i > 5; + } else { + StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value); + return ext != null && ext.getSnapshot().getElement().size() > 5; + } + } + + + + + 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(ProfileUtilities.UD_DERIVATION_POINTER)) { + return binding; + } + ElementDefinition base = (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); + if (!base.hasBinding()) { + return binding; + } + ElementDefinitionBindingComponent o = base.getBinding(); + ElementDefinitionBindingComponent b = new ElementDefinitionBindingComponent(); + b.setUserData(ProfileUtilities.UD_DERIVATION_POINTER, o); + if (binding.hasValueSet()) { + b.setValueSet(binding.getValueSet()); + } else if (o.hasValueSet()) { + b.setValueSet(o.getValueSet()); + b.getValueSetElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getValueSetElement()); + } + if (binding.hasStrength()) { + b.setStrength(binding.getStrength()); + } else if (o.hasStrength()) { + b.setStrength(o.getStrength()); + b.getStrengthElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getStrengthElement()); + } + if (binding.hasDescription()) { + b.setDescription(binding.getDescription()); + } else if (o.hasDescription()) { + b.setDescription(o.getDescription()); + b.getDescriptionElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getDescriptionElement()); + } + // todo: derivation? + b.getExtension().addAll(binding.getExtension()); + return b; + } + + private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) { + String ref = context.getPkp().getLinkFor(corePath, value.fhirType()); + if (ref != null && ref.contains(".html")) { + ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#"; + } else { + ref = "?gen-fv?"; + } + StructureDefinition sd = context.getWorker().fetchTypeDefinition(value.fhirType()); + + for (org.hl7.fhir.r5.model.Property t : value.children()) { + if (t.getValues().size() > 0 || snapshot) { + ElementDefinition ed = findElementDefinition(sd, t.getName()); + if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) { + if (!skipnoValue) { + Row row = gen.new Row(); + erow.getSubRows().add(row); + Cell c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : corePath+(VersionUtilities.isR5Ver(context.getWorker().getVersion()) ? "types-definitions.html#"+ed.getBase().getPath() : "element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); + c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece(null, null, null)); + c = gen.new Cell(); + row.getCells().add(c); + if (!pattern) { + c.addPiece(gen.new Piece(null, "0..0", null)); + row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); + } else if (isPrimitive(t.getTypeCode())) { + row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); + } else if (isReference(t.getTypeCode())) { + row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); + } else { + row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); + } + c = gen.new Cell(); + row.getCells().add(c); + if (t.getTypeCode().contains("(")) { + String tc = t.getTypeCode(); + String tn = tc.substring(0, tc.indexOf("(")); + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); + c.addPiece(gen.new Piece(null, "(", null)); + String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); + for (String s : p) { + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); + } + c.addPiece(gen.new Piece(null, ")", null)); + } else { + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, t.getTypeCode()), t.getTypeCode(), null)); + } + c = gen.new Cell(); + c.addPiece(gen.new Piece(null, ed.getShort(), null)); + row.getCells().add(c); + } + } else { + for (Base b : t.getValues()) { + Row row = gen.new Row(); + erow.getSubRows().add(row); + row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); + + Cell c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : (VersionUtilities.isR5Ver(context.getWorker().getVersion()) ? corePath+"types-definitions.html#"+ed.getBase().getPath() : corePath+"element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); + + c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece(null, null, null)); + + c = gen.new Cell(); + row.getCells().add(c); + if (pattern) + c.addPiece(gen.new Piece(null, "1.."+(t.getMaxCardinality() == 2147483647 ? "*" : Integer.toString(t.getMaxCardinality())), null)); + else + c.addPiece(gen.new Piece(null, "1..1", null)); + + c = gen.new Cell(); + row.getCells().add(c); + if (b.fhirType().contains("(")) { + String tc = b.fhirType(); + String tn = tc.substring(0, tc.indexOf("(")); + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); + c.addPiece(gen.new Piece(null, "(", null)); + String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); + for (String s : p) { + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); + } + c.addPiece(gen.new Piece(null, ")", null)); + } else { + c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, b.fhirType()), b.fhirType(), null)); + } + + if (b.isPrimitive()) { + c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece(null, ed.getShort(), null)); + c.addPiece(gen.new Piece("br")); + c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); + String s = b.primitiveValue(); + // ok. let's see if we can find a relevant link for this + String link = null; + if (Utilities.isAbsoluteUrl(s)) { + link = context.getPkp().getLinkForUrl(corePath, s); + } + c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen")); + } else { + c = gen.new Cell(); + row.getCells().add(c); + c.addPiece(gen.new Piece(null, ed.getShort(), null)); + c.addPiece(gen.new Piece("br")); + c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); + c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen")); + genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, skipnoValue); + } + } + } + } + } + } + + + private ElementDefinition findElementDefinition(StructureDefinition sd, String name) { + String path = sd.getTypeName()+"."+name; + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(path)) + return ed; + } + throw new FHIRException(context.getWorker().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT_, path)); + } + + + private String getFixedUrl(StructureDefinition sd) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals("Extension.url")) { + if (ed.hasFixed() && ed.getFixed() instanceof UriType) + return ed.getFixed().primitiveValue(); + } + } + return null; + } + + + private Piece describeCoded(HierarchicalTableGenerator gen, DataType fixed) { + if (fixed instanceof Coding) { + Coding c = (Coding) fixed; + ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); + if (vr.getDisplay() != null) + return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); + } else if (fixed instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) fixed; + for (Coding c : cc.getCoding()) { + ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); + if (vr.getDisplay() != null) + return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); + } + } + return null; + } + + + private boolean hasDescription(DataType fixed) { + if (fixed instanceof Coding) { + return ((Coding) fixed).hasDisplay(); + } else if (fixed instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) fixed; + if (cc.hasText()) + return true; + for (Coding c : cc.getCoding()) + if (c.hasDisplay()) + return true; + } // (fixed instanceof CodeType) || (fixed instanceof Quantity); + return false; + } + + + private boolean isCoded(DataType fixed) { + return (fixed instanceof Coding) || (fixed instanceof CodeableConcept) || (fixed instanceof CodeType) || (fixed instanceof Quantity); + } + + + private Cell generateGridDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, ElementDefinition valueDefn) throws IOException, FHIRException { + Cell c = gen.new Cell(); + row.getCells().add(c); + + if (used) { + if (definition.hasContentReference()) { + ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), definition.getContentReference(), profile); + if (ed == null) + c.getPieces().add(gen.new Piece(null, "Unknown reference to "+definition.getContentReference(), null)); + else { + if (ed.getSource() == profile) { + c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), "See "+ed.getElement().getPath(), null)); + } else { + c.getPieces().add(gen.new Piece(ed.getSource().getUserData("path")+"#"+ed.getElement().getPath(), "See "+ed.getSource().getTypeName()+"."+ed.getElement().getPath(), null)); + } + } + } + if (definition.getPath().endsWith("url") && definition.hasFixed()) { + c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); + } else { + if (url != null) { + if (!c.getPieces().isEmpty()) + c.addPiece(gen.new Piece("br")); + String fullUrl = url.startsWith("#") ? baseURL+url : url; + StructureDefinition ed = context.getWorker().fetchResource(StructureDefinition.class, url, profile); + String ref = null; + if (ed != null) { + String p = ed.getUserString("path"); + if (p != null) { + ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p); + } + } + c.getPieces().add(gen.new Piece(null, "URL: ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(ref, fullUrl, null)); + } + + if (definition.hasSlicing()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); + } + if (definition != null) { + ElementDefinitionBindingComponent binding = null; + if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) + binding = valueDefn.getBinding(); + else if (definition.hasBinding()) + binding = definition.getBinding(); + if (binding!=null && !binding.isEmpty()) { + if (!c.getPieces().isEmpty()) + c.addPiece(gen.new Piece("br")); + BindingResolution br = context.getPkp().resolveBinding(profile, binding, definition.getPath()); + c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, "Binding: ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null))); + if (binding.hasStrength()) { + c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null))); + c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null)); + } + if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { + c.getPieces().add(gen.new Piece(null, ": ", null)); + c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue()); + } + } + for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); + if (inv.getHumanElement().hasExtension(ToolingExtensions.EXT_REND_MD)) { + c.addMarkdown(inv.getHumanElement().getExtensionString(ToolingExtensions.EXT_REND_MD)); + } else { + c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); + } + } + if (definition.hasFixed()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold"))); + String s = buildJson(definition.getFixed()); + String link = null; + if (Utilities.isAbsoluteUrl(s)) + link = context.getPkp().getLinkForUrl(corePath, s); + c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); + } else if (definition.hasPattern()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); + } else if (definition.hasExample()) { + for (ElementDefinitionExampleComponent ex : definition.getExample()) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, "Example'"+("".equals("General")? "" : " "+ex.getLabel()+"'")+": ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); + } + } + if (definition.hasMaxLength() && definition.getMaxLength()!=0) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); + } + if (profile != null) { + for (StructureDefinitionMappingComponent md : profile.getMapping()) { + if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { + ElementDefinitionMappingComponent map = null; + for (ElementDefinitionMappingComponent m : definition.getMapping()) + if (m.getIdentity().equals(md.getIdentity())) + map = m; + if (map != null) { + for (int i = 0; i list, ElementDefinition e) { + return (!e.hasSliceName() && !e.hasSlicing() && (onlyInformationIsMapping(e))) && + getChildren(list, e).isEmpty(); + } + + private boolean onlyInformationIsMapping(ElementDefinition d) { + return !d.hasShort() && !d.hasDefinition() && + !d.hasRequirements() && !d.getAlias().isEmpty() && !d.hasMinElement() && + !d.hasMax() && !d.getType().isEmpty() && !d.hasContentReference() && + !d.hasExample() && !d.hasFixed() && !d.hasMaxLengthElement() && + !d.getCondition().isEmpty() && !d.getConstraint().isEmpty() && !d.hasMustSupportElement() && + !d.hasBinding(); + } + + private boolean allAreReference(List types) { + for (TypeRefComponent t : types) { + if (!t.hasTarget()) + return false; + } + return true; + } + + private List getChildren(List all, ElementDefinition element) { + List result = new ArrayList(); + int i = all.indexOf(element)+1; + while (i < all.size() && all.get(i).getPath().length() > element.getPath().length()) { + if ((all.get(i).getPath().substring(0, element.getPath().length()+1).equals(element.getPath()+".")) && !all.get(i).getPath().substring(element.getPath().length()+1).contains(".")) + result.add(all.get(i)); + i++; + } + return result; + } + + + protected String tail(String path) { + if (path == null) { + return ""; + } else if (path.contains(".")) + return path.substring(path.lastIndexOf('.')+1); + else + return path; + } + + + + protected boolean isPrimitive(String value) { + StructureDefinition sd = context.getWorker().fetchTypeDefinition(value); + if (sd == null) // might be running before all SDs are available + return Utilities.existsInList(value, "base64Binary", "boolean", "canonical", "code", "date", "dateTime", "decimal", "id", "instant", "integer", "integer64", "markdown", "oid", "positiveInt", "string", "time", "unsignedInt", "uri", "url", "uuid"); + else + return sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; + } + + + private boolean isDataType(String value) { + StructureDefinition sd = context.getWorker().fetchTypeDefinition(value); + if (sd == null) // might be running before all SDs are available + return Utilities.existsInList(value, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing", + "ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext"); + else + return sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION; + } + + private boolean slicesExist(List elements, ElementDefinition element) { + if (elements == null) { + return true; + } + boolean found = false; + int start = elements.indexOf(element); + if (start < 0) { + return false; + } + for (int i = start; i < elements.size(); i++) { + ElementDefinition ed = elements.get(i); + if (ed.getPath().equals(element.getPath())) { + if (ed.hasSliceName()) { + found = true; + } + } + if (ed.getPath().length() < element.getPath().length()) { + break; + } + } + return found; + } + + + private Cell addCell(Row row, Cell cell) { + row.getCells().add(cell); + return (cell); + } + + private String checkAdd(String src, String app) { + return app == null ? src : src + app; + } + + private boolean hasNonBaseConditions(List conditions) { + for (IdType c : conditions) { + if (!isBaseCondition(c)) { + return true; + } + } + return false; + } + + + private boolean hasNonBaseConstraints(List constraints) { + for (ElementDefinitionConstraintComponent c : constraints) { + if (!isBaseConstraint(c)) { + return true; + } + } + return false; + } + + private String listConstraintsAndConditions(ElementDefinition element) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ElementDefinitionConstraintComponent con : element.getConstraint()) { + if (!isBaseConstraint(con)) { + b.append(con.getKey()); + } + } + for (IdType id : element.getCondition()) { + if (!isBaseCondition(id)) { + b.append(id.asStringValue()); + } + } + return b.toString(); + } + + private boolean isBaseCondition(IdType c) { + String key = c.asStringValue(); + 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 != 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, Resource src) { + // create a child for each choice + for (TypeRefComponent tr : element.getType()) { + if (!mustSupportMode || allTypesMustSupport(element) || isMustSupport(tr)) { + Row choicerow = gen.new Row(); + String t = tr.getWorkingCode(); + if (isReference(t)) { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); + choicerow.getCells().add(gen.new Cell()); + choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); + choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + Cell c = gen.new Cell(); + choicerow.getCells().add(c); + if (ADD_REFERENCE_TO_TABLE) { + if (tr.getWorkingCode().equals("canonical")) + c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); + else + c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); + if (!mustSupportMode && isMustSupportDirect(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + c.getPieces().add(gen.new Piece(null, "(", null)); + } + boolean first = true; + for (CanonicalType rt : tr.getTargetProfile()) { + if (!mustSupportMode || allProfilesMustSupport(tr.getTargetProfile()) || isMustSupport(rt)) { + if (!first) + c.getPieces().add(gen.new Piece(null, " | ", null)); + genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue(), src); + if (!mustSupportMode && isMustSupport(rt) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } + first = false; + } + } + if (first) { + c.getPieces().add(gen.new Piece(null, "Any", null)); + } + + if (ADD_REFERENCE_TO_TABLE) { + c.getPieces().add(gen.new Piece(null, ")", null)); + } + + } else { + StructureDefinition sd = context.getWorker().fetchTypeDefinition(t); + if (sd == null) { + System.out.println("Unable to find "+t); + sd = context.getWorker().fetchTypeDefinition(t); + } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); + choicerow.getCells().add(gen.new Cell()); + choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); + choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getTypeName(), null, null); + choicerow.getCells().add(c); + if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + } else { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); + choicerow.getCells().add(gen.new Cell()); + choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); + choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + Cell c = gen.new Cell(null, context.getPkp().getLinkFor(corePath, t), sd.getTypeName(), null, null); + choicerow.getCells().add(c); + if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + } + if (tr.hasProfile()) { + Cell typeCell = choicerow.getCells().get(3); + typeCell.addPiece(gen.new Piece(null, "(", null)); + boolean first = true; + for (CanonicalType pt : tr.getProfile()) { + if (!mustSupportMode || allProfilesMustSupport(tr.getProfile()) || isMustSupport(pt)) { + if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); + StructureDefinition psd = context.getWorker().fetchResource(StructureDefinition.class, pt.getValue(), src); + if (psd == null) + typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); + else + typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); + if (!mustSupportMode && isMustSupport(pt) && element.getMustSupport()) { + typeCell.addPiece(gen.new Piece(null, " ", null)); + typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } + } + } + typeCell.addPiece(gen.new Piece(null, ")", null)); + } + } + choicerow.getCells().add(gen.new Cell()); + subRows.add(choicerow); + } + } + } + + private boolean isReference(String t) { + return t.equals("Reference") || t.equals("canonical"); + } + + + + private List readChoices(ElementDefinition ed, List children) { + List result = new ArrayList<>(); + for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { + ElementChoiceGroup grp = context.getProfileUtilities().processConstraint(children, c); + if (grp != null) { + result.add(grp); + } + } + return result; + } + + private Piece checkForNoChange(Element src1, Element src2, Piece piece) { + if (src1.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS) && src2.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS)) { + piece.addStyle("opacity: 0.5"); + } + return piece; + } + + + private String buildJson(DataType value) throws IOException { + if (value instanceof PrimitiveType) + return ((PrimitiveType) value).asStringValue(); + + IParser json = new JsonParser(); + return json.composeString(value, null); + } + + private String describeSlice(ElementDefinitionSlicingComponent slicing) { + return translate("sd.table", "%s, %s by %s", slicing.getOrdered() ? translate("sd.table", "Ordered") : translate("sd.table", "Unordered"), describe(slicing.getRules()), commas(slicing.getDiscriminator())); + } + + + + private String commas(List list) { + CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); + for (ElementDefinitionSlicingDiscriminatorComponent id : list) + c.append((id.hasType() ? id.getType().toCode() : "??")+":"+id.getPath()); + return c.toString(); + } + + + private String describe(SlicingRules rules) { + if (rules == null) + return translate("sd.table", "Unspecified"); + switch (rules) { + case CLOSED : return translate("sd.table", "Closed"); + case OPEN : return translate("sd.table", "Open"); + case OPENATEND : return translate("sd.table", "Open At End"); + default: + return "?gen-sr?"; + } + } + + private static boolean allTypesMustSupport(ElementDefinition e) { + boolean all = true; + boolean any = false; + for (TypeRefComponent tr : e.getType()) { + all = all && isMustSupport(tr); + any = any || isMustSupport(tr); + } + return !all && !any; + } + + private static boolean allProfilesMustSupport(List profiles) { + boolean all = true; + boolean any = false; + for (CanonicalType u : profiles) { + all = all && isMustSupport(u); + any = any || isMustSupport(u); + } + return !all && !any; + } + private static boolean isMustSupportDirect(TypeRefComponent tr) { + return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); + } + + private static boolean isMustSupport(TypeRefComponent tr) { + if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) { + return true; + } + if (isMustSupport(tr.getProfile())) { + return true; + } + return isMustSupport(tr.getTargetProfile()); + } + + private static boolean isMustSupport(List profiles) { + for (CanonicalType ct : profiles) { + if (isMustSupport(ct)) { + return true; + } + } + return false; + } + + + private static boolean isMustSupport(CanonicalType profile) { + return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT)); + } + + + + private SpanEntry buildSpanEntryFromProfile(String name, String cardinality, StructureDefinition profile) throws IOException { + SpanEntry res = new SpanEntry(); + res.setName(name); + res.setCardinality(cardinality); + res.setProfileLink(profile.getUserString("path")); + res.setResType(profile.getTypeName()); + StructureDefinition base = context.getWorker().fetchResource(StructureDefinition.class, res.getResType()); + if (base != null) + res.setResLink(base.getUserString("path")); + res.setId(profile.getId()); + res.setProfile(profile.getDerivation() == TypeDerivationRule.CONSTRAINT); + StringBuilder b = new StringBuilder(); + b.append(res.getResType()); + boolean first = true; + boolean open = false; + if (profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { + res.setDescription(profile.getName()); + for (ElementDefinition ed : profile.getSnapshot().getElement()) { + if (isKeyProperty(ed.getBase().getPath()) && ed.hasFixed()) { + if (first) { + open = true; + first = false; + b.append("["); + } else { + b.append(", "); + } + b.append(tail(ed.getBase().getPath())); + b.append("="); + b.append(summarize(ed.getFixed())); + } + } + if (open) + b.append("]"); + } else + res.setDescription("Base FHIR "+profile.getName()); + res.setType(b.toString()); + return res ; + } + + + private String summarize(DataType value) throws IOException { + if (value instanceof Coding) + return summarizeCoding((Coding) value); + else if (value instanceof CodeableConcept) + return summarizeCodeableConcept((CodeableConcept) value); + else + return buildJson(value); + } + + + private String summarizeCoding(Coding value) { + String uri = value.getSystem(); + String system = TerminologyRenderer.describeSystem(uri); + if (Utilities.isURL(system)) { + if (system.equals("http://cap.org/protocols")) + system = "CAP Code"; + } + return system+" "+value.getCode(); + } + + + private String summarizeCodeableConcept(CodeableConcept value) { + if (value.hasCoding()) + return summarizeCoding(value.getCodingFirstRep()); + else + return value.getText(); + } + + + private boolean isKeyProperty(String path) { + return Utilities.existsInList(path, "Observation.code"); + } + + + private TableModel initSpanningTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, String id) { + TableModel model = gen.new TableModel(id, true); + + model.setDocoImg(prefix+"help16.png"); + model.setDocoRef(Utilities.pathURL(prefix, "formats.html#table")); // todo: change to graph definition + model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Property", "A profiled resource", null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Card.", "Minimum and Maximum # of times the the element can appear in the instance", null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Content", "What goes here", null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Description", "Description of the profile", null, 0)); + return model; + } + + private void genSpanEntry(HierarchicalTableGenerator gen, List rows, SpanEntry span) throws IOException { + Row row = gen.new Row(); + rows.add(row); + row.setAnchor(span.getId()); + //row.setColor(..?); + if (span.isProfile()) { + row.setIcon("icon_profile.png", HierarchicalTableGenerator.TEXT_ICON_PROFILE); + } else { + row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + } + + row.getCells().add(gen.new Cell(null, null, span.getName(), null, null)); + row.getCells().add(gen.new Cell(null, null, span.getCardinality(), null, null)); + row.getCells().add(gen.new Cell(null, span.getProfileLink(), span.getType(), null, null)); + row.getCells().add(gen.new Cell(null, null, span.getDescription(), null, null)); + + for (SpanEntry child : span.getChildren()) { + genSpanEntry(gen, row.getSubRows(), child); + } + } + + + public XhtmlNode generateSpanningTable(StructureDefinition profile, String imageFolder, boolean onlyConstraints, String constraintPrefix, Set outputTracker) throws IOException, FHIRException { + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, false, true); + gen.setTranslator(getTranslator()); + TableModel model = initSpanningTable(gen, "", false, profile.getId()); + Set processed = new HashSet(); + SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); + + genSpanEntry(gen, model.getRows(), span); + return gen.generate(model, "", 0, outputTracker); + } + + private SpanEntry buildSpanningTable(String name, String cardinality, StructureDefinition profile, Set processed, boolean onlyConstraints, String constraintPrefix) throws IOException { + SpanEntry res = buildSpanEntryFromProfile(name, cardinality, profile); + boolean wantProcess = !processed.contains(profile.getUrl()); + processed.add(profile.getUrl()); + if (wantProcess && profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { + for (ElementDefinition ed : profile.getSnapshot().getElement()) { + if (!"0".equals(ed.getMax()) && ed.getType().size() > 0) { + String card = getCardinality(ed, profile.getSnapshot().getElement()); + if (!card.endsWith(".0")) { + List refProfiles = listReferenceProfiles(ed); + if (refProfiles.size() > 0) { + String uri = refProfiles.get(0); + if (uri != null) { + StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, uri); + if (sd != null && (!onlyConstraints || (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && (constraintPrefix == null || sd.getUrl().startsWith(constraintPrefix))))) { + res.getChildren().add(buildSpanningTable(nameForElement(ed), card, sd, processed, onlyConstraints, constraintPrefix)); + } + } + } + } + } + } + } + return res; + } + + + private String getCardinality(ElementDefinition ed, List list) { + int min = ed.getMin(); + int max = !ed.hasMax() || ed.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(ed.getMax()); + ElementDefinition ned = ed; + while (ned != null && ned.getPath().contains(".")) { + ned = findParent(ned, list); + if (ned != null) { // todo: this can happen if we've walked into a resoruce. Not sure what to about that? + if ("0".equals(ned.getMax())) + max = 0; + else if (!ned.getMax().equals("1") && !ned.hasSlicing()) + max = Integer.MAX_VALUE; + if (ned.getMin() == 0) { + min = 0; + } + } + } + return Integer.toString(min)+".."+(max == Integer.MAX_VALUE ? "*" : Integer.toString(max)); + } + + + private ElementDefinition findParent(ElementDefinition ed, List list) { + int i = list.indexOf(ed)-1; + while (i >= 0 && !ed.getPath().startsWith(list.get(i).getPath()+".")) + i--; + if (i == -1) + return null; + else + return list.get(i); + } + + + private List listReferenceProfiles(ElementDefinition ed) { + List res = new ArrayList(); + for (TypeRefComponent tr : ed.getType()) { + // code is null if we're dealing with "value" and profile is null if we just have Reference() + if (tr.hasTarget() && tr.hasTargetProfile()) + for (UriType u : tr.getTargetProfile()) + res.add(u.getValue()); + } + return res; + } + + + private String nameForElement(ElementDefinition ed) { + return ed.getPath().substring(ed.getPath().indexOf(".")+1); + } + + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java index 864a7d4fa..6fcb3e81f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java @@ -11,8 +11,8 @@ import java.util.Map; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.Base; @@ -356,6 +356,9 @@ public class RenderingContext { public RenderingContext setProfileUtilities(ProfileUtilities profileUtilities) { this.profileUtilitiesR = profileUtilities; + if (pkp == null && profileUtilities.getPkp() != null) { + pkp = profileUtilities.getPkp(); + } return this; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java index 13b634924..3ef5835e4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java @@ -36,8 +36,8 @@ package org.hl7.fhir.r5.utils.structuremap; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java index 9056f0314..6610bcbbb 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PETests.java @@ -106,7 +106,7 @@ public class PETests { checkElement(gchildren.get(0), "extension", "extension", 0, Integer.MAX_VALUE, false, "http://hl7.org/fhir/StructureDefinition/Extension", 3, "extension"); checkElement(gchildren.get(1), "extension", "slice1", 0, 2, false, "http://hl7.org/fhir/StructureDefinition/Coding", 6, "extension('slice1').value"); checkElement(gchildren.get(2), "extension", "slice2", 0, Integer.MAX_VALUE, false, "http://hl7.org/fhir/StructureDefinition/string", 2, "extension('slice2').value"); - checkElement(gchildren.get(3), "extension", "slice3", 1, 1, false, "http://hl7.org/fhir/StructureDefinition/Extension", 3, "extension('slice3').extension"); + checkElement(gchildren.get(3), "extension", "slice3", 1, 1, false, "http://hl7.org/fhir/StructureDefinition/Extension", 3, "extension('slice3')"); ggchildren = gchildren.get(3).children("http://hl7.org/fhir/StructureDefinition/Extension"); checkElement(ggchildren.get(0), "extension", "extension", 0, Integer.MAX_VALUE, false, "http://hl7.org/fhir/StructureDefinition/Extension", 3, "extension"); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java index a6db55281..a0cf7cd49 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java @@ -11,8 +11,9 @@ import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index 3d4e6fc84..3b54e3f35 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -17,8 +17,9 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.r5.DiffUtils; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.test.utils.TestPackageLoader; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index c710dbb3c..a6fb05b77 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -16,8 +16,9 @@ import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Base; From f3fc19a90620a59f159e991fa91d4d5bef6f56d8 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Jan 2023 14:54:11 +1100 Subject: [PATCH 3/4] refactor profile generation (utils -> renderer) + add new views --- .../AdditionalBindingsRenderer.java | 71 ++- .../conformance/profile/ProfileUtilities.java | 175 ++++--- .../java/org/hl7/fhir/r5/model/Coding.java | 1 + .../hl7/fhir/r5/profilemodel/PEBuilder.java | 25 +- .../fhir/r5/profilemodel/PEDefinition.java | 41 +- .../r5/profilemodel/PEDefinitionElement.java | 7 +- .../profilemodel/PEDefinitionExtension.java | 11 +- .../r5/profilemodel/PEDefinitionResource.java | 4 +- .../r5/profilemodel/PEDefinitionSlice.java | 4 +- .../PEDefinitionSubExtension.java | 11 +- .../profilemodel/PEDefinitionTypeSlice.java | 4 +- .../hl7/fhir/r5/renderers/CodeResolver.java | 47 ++ .../hl7/fhir/r5/renderers/DataRenderer.java | 48 +- .../StructureDefinitionRenderer.java | 446 ++++++++++++++++-- .../r5/renderers/utils/RenderingContext.java | 18 + .../terminologies/JurisdictionUtilities.java | 4 + .../fhir/r5/test/utils/TestPackageLoader.java | 2 +- .../r5/test/NarrativeGenerationTests.java | 50 +- .../txCache/org.hl7.fhir.r5/iso3166.cache | 11 + .../xhtml/HierarchicalTableGenerator.java | 9 +- .../hl7/fhir/utilities/xhtml/XhtmlFluent.java | 195 ++++++++ .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 224 ++------- .../fhir/utilities/xhtml/XhtmlNodeList.java | 185 ++++++++ 23 files changed, 1238 insertions(+), 355 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeResolver.java create mode 100644 org.hl7.fhir.r5/src/test/resources/txCache/org.hl7.fhir.r5/iso3166.cache create mode 100644 org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlFluent.java create mode 100644 org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java index 9f0da47e9..b303a7f72 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/AdditionalBindingsRenderer.java @@ -4,18 +4,29 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Set; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.conformance.profile.BindingResolution; import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.PrimitiveType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.UsageContext; +import org.hl7.fhir.r5.renderers.CodeResolver; +import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; import org.hl7.fhir.r5.renderers.DataRenderer; import org.hl7.fhir.r5.renderers.IMarkdownProcessor; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.utils.PublicationHacker; +import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; @@ -23,6 +34,7 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import org.hl7.fhir.utilities.xhtml.XhtmlNodeList; public class AdditionalBindingsRenderer { public class AdditionalBindingDetail { @@ -78,14 +90,16 @@ public class AdditionalBindingsRenderer { private String path; private RenderingContext context; private IMarkdownProcessor md; + private CodeResolver cr; - public AdditionalBindingsRenderer(ProfileKnowledgeProvider pkp, String corePath, StructureDefinition profile, String path, RenderingContext context, IMarkdownProcessor md) { + public AdditionalBindingsRenderer(ProfileKnowledgeProvider pkp, String corePath, StructureDefinition profile, String path, RenderingContext context, IMarkdownProcessor md, CodeResolver cr) { this.pkp = pkp; this.corePath = corePath; this.profile = profile; this.path = path; this.context = context; this.md = md; + this.cr = cr; } public void seeMaxBinding(Extension ext) { @@ -349,5 +363,60 @@ public class AdditionalBindingsRenderer { return !bindings.isEmpty(); } + public void render(XhtmlNodeList children, List list) { + if (list.size() == 1) { + render(children, list.get(0)); + } else { + XhtmlNode ul = children.ul(); + for (ElementDefinitionBindingAdditionalComponent b : list) { + render(ul.li().getChildNodes(), b); + } + } + } + + private void render(XhtmlNodeList children, ElementDefinitionBindingAdditionalComponent b) { + if (b.getValueSet() == null) { + return; // what should happen? + } + BindingResolution br = pkp.resolveBinding(profile, b.getValueSet(), corePath); + XhtmlNode a = children.ah(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, b.hasDocumentation() ? b.getDocumentation() : null); + if (b.hasDocumentation()) { + a.attribute("title", b.getDocumentation()); + } + a.tx(br.display); + + if (b.hasShortDoco()) { + children.tx(": "); + children.tx(b.getShortDoco()); + } + if (b.getAny() || b.hasUsage()) { + children.tx(" ("); + boolean ffirst = !b.getAny(); + if (b.getAny()) { + children.tx("any repeat"); + } + for (UsageContext uc : b.getUsage()) { + if (ffirst) ffirst = false; else children.tx(","); + if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) { + children.tx(displayForUsage(uc.getCode())); + children.tx("="); + } + CodeResolution ccr = cr.resolveCode(uc.getValueCodeableConcept()); + children.ah(ccr.getLink(), ccr.getHint()).tx(ccr.getDisplay()); + } + children.tx(")"); + } + } + + + private String displayForUsage(Coding c) { + if (c.hasDisplay()) { + return c.getDisplay(); + } + if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) { + return c.getCode(); + } + return c.getCode(); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 01bb8b89d..a62f0f02e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -1604,7 +1604,7 @@ public class ProfileUtilities extends TranslatingUtilities { * @param element - the Element to update * @return - the updated Element */ - ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element) { + public ElementDefinition updateURLs(String url, String webUrl, ElementDefinition element) { if (element != null) { ElementDefinition defn = element; if (defn.hasBinding() && defn.getBinding().hasValueSet() && defn.getBinding().getValueSet().startsWith("#")) @@ -1634,7 +1634,7 @@ public class ProfileUtilities extends TranslatingUtilities { return element; } - private static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set baseFilenames, Set localFilenames, boolean processRelatives) { + public static String processRelativeUrls(String markdown, String webUrl, String basePath, List resourceNames, Set baseFilenames, Set localFilenames, boolean processRelatives) { if (markdown == null) { return ""; } @@ -2437,95 +2437,91 @@ public class ProfileUtilities extends TranslatingUtilities { return false; } -// there was some proposal that this be surfaced in the IG publisher, but it is not currently available -// private void closeDifferential(StructureDefinition base, StructureDefinition derived) throws FHIRException { -// for (ElementDefinition edb : base.getSnapshot().getElement()) { -// if (isImmediateChild(edb) && !edb.getPath().endsWith(".id")) { -// ElementDefinition edm = getMatchInDerived(edb, derived.getDifferential().getElement()); -// if (edm == null) { -// ElementDefinition edd = derived.getDifferential().addElement(); -// edd.setPath(edb.getPath()); -// edd.setMax("0"); -// } else if (edb.hasSlicing()) { -// closeChildren(base, edb, derived, edm); -// } -// } -// } -// sortDifferential(base, derived, derived.getName(), new ArrayList(), false); -// } + public void closeDifferential(StructureDefinition base, StructureDefinition derived) throws FHIRException { + for (ElementDefinition edb : base.getSnapshot().getElement()) { + if (isImmediateChild(edb) && !edb.getPath().endsWith(".id")) { + ElementDefinition edm = getMatchInDerived(edb, derived.getDifferential().getElement()); + if (edm == null) { + ElementDefinition edd = derived.getDifferential().addElement(); + edd.setPath(edb.getPath()); + edd.setMax("0"); + } else if (edb.hasSlicing()) { + closeChildren(base, edb, derived, edm); + } + } + } + sortDifferential(base, derived, derived.getName(), new ArrayList(), false); + } -// private void closeChildren(StructureDefinition base, ElementDefinition edb, StructureDefinition derived, ElementDefinition edm) { -//// String path = edb.getPath()+"."; -// int baseStart = base.getSnapshot().getElement().indexOf(edb); -// int baseEnd = findEnd(base.getSnapshot().getElement(), edb, baseStart+1); -// int diffStart = derived.getDifferential().getElement().indexOf(edm); -// int diffEnd = findEnd(derived.getDifferential().getElement(), edm, diffStart+1); -// -// for (int cBase = baseStart; cBase < baseEnd; cBase++) { -// ElementDefinition edBase = base.getSnapshot().getElement().get(cBase); -// if (isImmediateChild(edBase, edb)) { -// ElementDefinition edMatch = getMatchInDerived(edBase, derived.getDifferential().getElement(), diffStart, diffEnd); -// if (edMatch == null) { -// ElementDefinition edd = derived.getDifferential().addElement(); -// edd.setPath(edBase.getPath()); -// edd.setMax("0"); -// } else { -// closeChildren(base, edBase, derived, edMatch); -// } -// } -// } -// } + private void closeChildren(StructureDefinition base, ElementDefinition edb, StructureDefinition derived, ElementDefinition edm) { +// String path = edb.getPath()+"."; + int baseStart = base.getSnapshot().getElement().indexOf(edb); + int baseEnd = findEnd(base.getSnapshot().getElement(), edb, baseStart+1); + int diffStart = derived.getDifferential().getElement().indexOf(edm); + int diffEnd = findEnd(derived.getDifferential().getElement(), edm, diffStart+1); + + for (int cBase = baseStart; cBase < baseEnd; cBase++) { + ElementDefinition edBase = base.getSnapshot().getElement().get(cBase); + if (isImmediateChild(edBase, edb)) { + ElementDefinition edMatch = getMatchInDerived(edBase, derived.getDifferential().getElement(), diffStart, diffEnd); + if (edMatch == null) { + ElementDefinition edd = derived.getDifferential().addElement(); + edd.setPath(edBase.getPath()); + edd.setMax("0"); + } else { + closeChildren(base, edBase, derived, edMatch); + } + } + } + } -// -// -// -// private int findEnd(List list, ElementDefinition ed, int cursor) { -// String path = ed.getPath()+"."; -// while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { -// cursor++; -// } -// return cursor; -// } -// -// -// private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { -// for (ElementDefinition t : list) { -// if (t.getPath().equals(ed.getPath())) { -// return t; -// } -// } -// return null; -// } -// -// private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { -// for (int i = start; i < end; i++) { -// ElementDefinition t = list.get(i); -// if (t.getPath().equals(ed.getPath())) { -// return t; -// } -// } -// return null; -// } -// -// -// private boolean isImmediateChild(ElementDefinition ed) { -// String p = ed.getPath(); -// if (!p.contains(".")) { -// return false; -// } -// p = p.substring(p.indexOf(".")+1); -// return !p.contains("."); -// } -// -// private boolean isImmediateChild(ElementDefinition candidate, ElementDefinition base) { -// String p = candidate.getPath(); -// if (!p.contains(".")) -// return false; -// if (!p.startsWith(base.getPath()+".")) -// return false; -// p = p.substring(base.getPath().length()+1); -// return !p.contains("."); -// } + private int findEnd(List list, ElementDefinition ed, int cursor) { + String path = ed.getPath()+"."; + while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { + cursor++; + } + return cursor; + } + + + private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { + for (ElementDefinition t : list) { + if (t.getPath().equals(ed.getPath())) { + return t; + } + } + return null; + } + + private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { + for (int i = start; i < end; i++) { + ElementDefinition t = list.get(i); + if (t.getPath().equals(ed.getPath())) { + return t; + } + } + return null; + } + + + private boolean isImmediateChild(ElementDefinition ed) { + String p = ed.getPath(); + if (!p.contains(".")) { + return false; + } + p = p.substring(p.indexOf(".")+1); + return !p.contains("."); + } + + private boolean isImmediateChild(ElementDefinition candidate, ElementDefinition base) { + String p = candidate.getPath(); + if (!p.contains(".")) + return false; + if (!p.startsWith(base.getPath()+".")) + return false; + p = p.substring(base.getPath().length()+1); + return !p.contains("."); + } // public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set outputTracker, RenderingContext rc) throws IOException, FHIRException { // HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); @@ -3980,4 +3976,5 @@ public class ProfileUtilities extends TranslatingUtilities { return null; } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java index 633b6b99f..faa6ddf8a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java @@ -613,5 +613,6 @@ public class Coding extends DataType implements IBaseCoding, ICompositeType, ICo } // end addition + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java index 11b814ae8..4370ce845 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java @@ -120,7 +120,7 @@ public class PEBuilder { if (!profile.hasSnapshot()) { throw new DefinitionException("Profile '"+profile.getVersionedUrl()+"' does not have a snapshot"); } - return new PEDefinitionResource(this, profile); + return new PEDefinitionResource(this, profile, profile.getName()); } /** @@ -145,7 +145,7 @@ public class PEBuilder { if (!profile.hasSnapshot()) { throw new DefinitionException("Profile '"+url+"' does not have a snapshot"); } - return new PEDefinitionResource(this, profile); + return new PEDefinitionResource(this, profile, profile.getName()); } /** @@ -170,7 +170,7 @@ public class PEBuilder { if (!profile.hasSnapshot()) { throw new DefinitionException("Profile '"+url+"' does not have a snapshot"); } - return new PEDefinitionResource(this, profile); + return new PEDefinitionResource(this, profile, profile.getName()); } /** @@ -321,7 +321,7 @@ public class PEBuilder { ElementDefinition defn = list.get(i); if (!defn.getMax().equals("0") && (allFixed || include(defn))) { if (passElementPropsCheck(defn) && !Utilities.existsInList(defn.getName(), omitList)) { - PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn); + PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path()); pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension"))); if (cu.isPrimitiveDatatype(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { pe.setMustHaveValue(definition.getMustHaveValue()); @@ -335,11 +335,11 @@ public class PEBuilder { while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { StructureDefinition ext = getExtensionDefinition(list.get(i)); if (ext != null) { - res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext)); + res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext, parent.path())); } else if (isTypeSlicing(defn)) { - res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn)); + res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); } else { - res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn)); + res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); } i++; } @@ -363,7 +363,6 @@ public class PEBuilder { } } - private boolean passElementPropsCheck(ElementDefinition bdefn) { switch (elementProps) { case EXTENSION: @@ -390,14 +389,14 @@ public class PEBuilder { } } - protected List listSlices(StructureDefinition profileStructure, ElementDefinition definition) { + protected List listSlices(StructureDefinition profileStructure, ElementDefinition definition, PEDefinition parent) { List list = pu.getSliceList(profileStructure, definition); List res = new ArrayList<>(); for (ElementDefinition ed : list) { if (profileStructure.getDerivation() == TypeDerivationRule.CONSTRAINT && profileStructure.getType().equals("Extension")) { - res.add(new PEDefinitionSubExtension(this, profileStructure, ed)); + res.add(new PEDefinitionSubExtension(this, profileStructure, ed, parent.path())); } else { - PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, ed); + PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, ed, parent.path()); pe.setRecursing(definition == ed || (profileStructure.getDerivation() == TypeDerivationRule.SPECIALIZATION && profileStructure.getType().equals("Extension"))); res.add(pe); } @@ -562,4 +561,8 @@ public class PEBuilder { public List exec(Resource resource, Base data, String fhirpath) { return fpe.evaluate(this, resource, resource, data, fhirpath); } + + public boolean isResource(String name) { + return cu.isResource(name); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java index 786be6b9b..911c390e4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinition.java @@ -7,14 +7,23 @@ import java.util.Map; import org.apache.xmlbeans.impl.xb.xsdschema.All; import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.profilemodel.PEDefinition.PEDefinitionElementMode; +import org.hl7.fhir.utilities.Utilities; public abstract class PEDefinition { + public enum PEDefinitionElementMode { + Resource, Element, DataType, Extension + } + protected PEBuilder builder; protected String name; + protected String path; protected StructureDefinition profile; protected ElementDefinition definition; protected List types; @@ -40,11 +49,12 @@ public abstract class PEDefinition { //// this.data = data; // } - protected PEDefinition(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition) { + protected PEDefinition(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, String ppath) { this.builder = builder; this.name = name; this.profile = profile; this.definition = definition; + this.path = path == null ? name : ppath+"."+name; } @@ -55,6 +65,13 @@ public abstract class PEDefinition { return name; } + /** + * @return The path of the element or slice in the profile (name.name.name...) + */ + public String path() { + return path; + } + /** * @return The name of the element in the resource (may be different to the slice name) */ @@ -239,6 +256,28 @@ public abstract class PEDefinition { return max() > 1; } + public PEDefinitionElementMode mode() { + if (builder.isResource(definition.getBase().getPath())) { + return PEDefinitionElementMode.Resource; + } + for (TypeRefComponent tr : definition.getType()) { + if ("Extension".equals(tr.getWorkingCode())) { + return PEDefinitionElementMode.Extension; + } + if (!Utilities.existsInList(tr.getWorkingCode(), "Element", "BackboneElement")) { + return PEDefinitionElementMode.DataType; + } + } + return PEDefinitionElementMode.Element; + } + + /** + * @return true if this element is profiled one way or another + */ + public boolean isProfiled() { + return !profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition"); + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java index c1cba339d..c4cb4475b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java @@ -10,8 +10,8 @@ import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; public class PEDefinitionElement extends PEDefinition { - public PEDefinitionElement(PEBuilder builder, StructureDefinition profile, ElementDefinition definition) { - super(builder, definition.getName(), profile, definition); + public PEDefinitionElement(PEBuilder builder, StructureDefinition profile, ElementDefinition definition, String ppath) { + super(builder, definition.getName(), profile, definition, ppath); } @Override @@ -38,7 +38,7 @@ public class PEDefinitionElement extends PEDefinition { if (definition.hasSlicing()) { // get all the slices CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); - List slices = builder.listSlices(profile, definition); + List slices = builder.listSlices(profile, definition, this); // list all the fhirpaths for (PEDefinition slice : slices) { b.append("("+builder.makeSliceExpression(profile, definition.getSlicing(), slice.definition())+")"); @@ -52,4 +52,5 @@ public class PEDefinitionElement extends PEDefinition { } } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionExtension.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionExtension.java index d06c80d63..bb66d9d8f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionExtension.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionExtension.java @@ -6,6 +6,8 @@ import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.profilemodel.PEDefinition.PEDefinitionElementMode; +import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.r5.model.StructureDefinition; public class PEDefinitionExtension extends PEDefinition { @@ -15,8 +17,8 @@ public class PEDefinitionExtension extends PEDefinition { private ElementDefinition eed; private ElementDefinition ved; - public PEDefinitionExtension(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, ElementDefinition sliceDefinition, StructureDefinition extension) { - super(builder, name, profile, definition); + public PEDefinitionExtension(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, ElementDefinition sliceDefinition, StructureDefinition extension, String ppath) { + super(builder, name, profile, definition, ppath); this.sliceDefinition = sliceDefinition; this.extension= extension; eed = extension.getSnapshot().getElementByPath("Extension.extension"); @@ -48,7 +50,7 @@ public class PEDefinitionExtension extends PEDefinition { if (eed.getSlicing().getRules() != SlicingRules.CLOSED) { children.addAll(builder.listChildren(allFixed, this, extension, eed, "http://hl7.org/fhir/StructureDefinition/Extension", "value[x]", "url")); } - children.addAll(builder.listSlices(extension, eed)); + children.addAll(builder.listSlices(extension, eed, this)); } } @@ -61,4 +63,7 @@ public class PEDefinitionExtension extends PEDefinition { } } + public PEDefinitionElementMode mode() { + return PEDefinitionElementMode.Extension; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionResource.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionResource.java index d550b66d3..b7a6babe2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionResource.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionResource.java @@ -6,8 +6,8 @@ import org.hl7.fhir.r5.model.StructureDefinition; public class PEDefinitionResource extends PEDefinition { - public PEDefinitionResource(PEBuilder builder, StructureDefinition profile) { - super(builder, profile.getName(), profile, profile.getSnapshot().getElementFirstRep()); + public PEDefinitionResource(PEBuilder builder, StructureDefinition profile, String ppath) { + super(builder, profile.getName(), profile, profile.getSnapshot().getElementFirstRep(), ppath); } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSlice.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSlice.java index 65bcf8934..a5b6003f5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSlice.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSlice.java @@ -11,8 +11,8 @@ public class PEDefinitionSlice extends PEDefinition { protected ElementDefinition sliceDefinition; - public PEDefinitionSlice(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition profileDefinition, ElementDefinition sliceDefinition) { - super(builder, name, profile, profileDefinition); + public PEDefinitionSlice(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition profileDefinition, ElementDefinition sliceDefinition, String ppath) { + super(builder, name, profile, profileDefinition, ppath); this.sliceDefinition = sliceDefinition; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java index c195eccb3..4b8bcb128 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionSubExtension.java @@ -6,6 +6,7 @@ import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.profilemodel.PEDefinition.PEDefinitionElementMode; import org.hl7.fhir.r5.model.StructureDefinition; public class PEDefinitionSubExtension extends PEDefinition { @@ -14,8 +15,8 @@ public class PEDefinitionSubExtension extends PEDefinition { private ElementDefinition ved; private ElementDefinition ued; - public PEDefinitionSubExtension(PEBuilder builder, StructureDefinition profile, ElementDefinition definition) { - super(builder, definition.getSliceName(), profile, definition); + public PEDefinitionSubExtension(PEBuilder builder, StructureDefinition profile, ElementDefinition definition, String ppath) { + super(builder, definition.getSliceName(), profile, definition, ppath); List childDefs = builder.getChildren(profile, definition); eed = getElementByName(childDefs, "extension"); ved = getElementByName(childDefs, "value[x]"); @@ -56,7 +57,7 @@ public class PEDefinitionSubExtension extends PEDefinition { if (eed.getSlicing().getRules() != SlicingRules.CLOSED) { children.addAll(builder.listChildren(allFixed, this, profile, eed, "http://hl7.org/fhir/StructureDefinition/Extension", "value[x]", "url")); } - children.addAll(builder.listSlices(profile, eed)); + children.addAll(builder.listSlices(profile, eed, this)); } } @@ -69,4 +70,8 @@ public class PEDefinitionSubExtension extends PEDefinition { } } + public PEDefinitionElementMode mode() { + return PEDefinitionElementMode.Extension; + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionTypeSlice.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionTypeSlice.java index 83e5ce3fa..02d44f021 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionTypeSlice.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionTypeSlice.java @@ -12,8 +12,8 @@ public class PEDefinitionTypeSlice extends PEDefinition { protected ElementDefinition sliceDefinition; - public PEDefinitionTypeSlice(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, ElementDefinition sliceDefinition) { - super(builder, name, profile, definition); + public PEDefinitionTypeSlice(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, ElementDefinition sliceDefinition, String ppath) { + super(builder, name, profile, definition, ppath); this.sliceDefinition = sliceDefinition; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeResolver.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeResolver.java new file mode 100644 index 000000000..0e1390cc3 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeResolver.java @@ -0,0 +1,47 @@ +package org.hl7.fhir.r5.renderers; + +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; + +public interface CodeResolver { + + public class CodeResolution { + private String systenName; + private String systemLink; + private String link; + private String display; + private String hint; + + + protected CodeResolution(String systenName, String systemLink, String link, String display, String hint) { + super(); + this.systenName = systenName; + this.systemLink = systemLink; + this.link = link; + this.display = display; + this.hint = hint; + } + + public String getSystenName() { + return systenName; + } + public String getSystemLink() { + return systemLink; + } + public String getLink() { + return link; + } + public String getDisplay() { + return display; + } + public String getHint() { + return hint; + } + + + } + + public CodeResolution resolveCode(String system, String code); + public CodeResolution resolveCode(Coding code); + public CodeResolution resolveCode(CodeableConcept code); +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index 810e5f513..d84f984d5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -70,9 +70,11 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; +import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; @@ -84,7 +86,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlParser; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; -public class DataRenderer extends Renderer { +public class DataRenderer extends Renderer implements CodeResolver { // -- 1. context -------------------------------------------------------------- @@ -301,6 +303,9 @@ public class DataRenderer extends Renderer { } private String lookupCode(String system, String version, String code) { + if (JurisdictionUtilities.isJurisdiction(system)) { + return JurisdictionUtilities.displayJurisdiction(system+"#"+code); + } ValidationResult t = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().setVersionFlexible(true), system, version, code, null); if (t != null && t.getDisplay() != null) @@ -947,6 +952,12 @@ public class DataRenderer extends Renderer { } else { return "https://www.nlm.nih.gov/research/umls/rxnorm/index.html"; } + } else if ("urn:iso:std:iso:3166".equals(system)) { + if (!Utilities.noString(code)) { + return "https://en.wikipedia.org/wiki/ISO_3166-2:"+code; + } else { + return "https://en.wikipedia.org/wiki/ISO_3166-2"; + } } else { CodeSystem cs = context.getWorker().fetchCodeSystem(system, version); if (cs != null && cs.hasUserData("path")) { @@ -960,6 +971,41 @@ public class DataRenderer extends Renderer { return null; } + public CodeResolution resolveCode(String system, String code) { + return resolveCode(new Coding().setSystem(system).setCode(code)); + } + + public CodeResolution resolveCode(Coding c) { + String systemName; + String systemLink; + String link; + String display = null; + String hint; + + if (c.hasDisplayElement()) + display = c.getDisplay(); + if (Utilities.noString(display)) + display = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + if (Utilities.noString(display)) { + display = c.getCode(); + } + + CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem()); + systemLink = cs != null ? cs.getUserString("path") : null; + systemName = cs != null ? cs.present() : describeSystem(c.getSystem()); + link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); + + hint = systemName+": "+display+(c.hasVersion() ? " (version = "+c.getVersion()+")" : ""); + return new CodeResolution(systemName, systemLink, link, display, hint); + } + + public CodeResolution resolveCode(CodeableConcept code) { + if (code.hasCoding()) { + return resolveCode(code.getCodingFirstRep()); + } else { + return new CodeResolution(null, null, null, code.getText(), code.getText()); + } + } protected void renderCodingWithDetails(XhtmlNode x, Coding c) { String s = ""; if (c.hasDisplayElement()) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index ecc4e9612..78399e4b8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -13,12 +13,15 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.conformance.AdditionalBindingsRenderer; import org.hl7.fhir.r5.conformance.profile.BindingResolution; +import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementChoiceGroup; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ExtensionContext; +import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.ActorDefinition; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeType; @@ -27,12 +30,15 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.Element; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.AdditionalBindingPurposeVS; import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionExampleComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionObligationComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; @@ -51,10 +57,13 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; +import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.utils.PublicationHacker; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -69,8 +78,10 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Title; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import org.hl7.fhir.utilities.xhtml.XhtmlNodeList; public class StructureDefinitionRenderer extends ResourceRenderer { @@ -122,8 +133,8 @@ public class StructureDefinitionRenderer extends ResourceRenderer { // private static final int AGG_IND = 1; // private static final int AGG_GR = 2; // private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; - private static final String CONSTRAINT_CHAR = "C"; - private static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; + public static final String CONSTRAINT_CHAR = "C"; + public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; private final boolean ADD_REFERENCE_TO_TABLE = true; private boolean useTableForFixedValues = true; @@ -266,13 +277,33 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } - + private static class Column { + String id; + String title; + String hint; + private String link; + + protected Column(String id, String title, String hint) { + super(); + this.id = id; + this.title = title; + this.hint = hint; + } + protected Column(String id, String title, String hint, String link) { + super(); + this.id = id; + this.title = title; + this.hint = hint; + this.link = link; + } + + } public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException { assert(diff != snapshot);// check it's ok to get rid of one of these HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); gen.setTranslator(getTranslator()); - TableModel model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER); + List list; if (diff) list = supplementMissingDiffElements(profile); @@ -280,11 +311,30 @@ public class StructureDefinitionRenderer extends ResourceRenderer { list = new ArrayList<>(); list.addAll(profile.getSnapshot().getElement()); } + + List columns = new ArrayList<>(); + TableModel model; + switch (context.getStructureMode()) { + case BINDINGS: + scanBindings(columns, list); + model = initCustomTable(gen, corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER, columns); + break; + case OBLIGATIONS: + scanObligations(columns, list); + model = initCustomTable(gen, corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER, columns); + break; + case SUMMARY: + model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER); + break; + default: + throw new Error("Unknown structure mode"); + } + List profiles = new ArrayList(); profiles.add(profile); keyRows.clear(); - genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile); + genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile, columns); try { return gen.generate(model, imagePath, 0, outputTracker); } catch (org.hl7.fhir.exceptions.FHIRException e) { @@ -292,8 +342,129 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } + private void scanBindings(List columns, List list) { + Set cols = new HashSet<>(); + scanBindings(cols, list, list.get(0)); + if (cols.contains("required")) { + columns.add(new Column("required", "Required", "Concepts must come from this value set")); + } + if (cols.contains("extensible")) { + columns.add(new Column("extensible", "Extensible", "Concepts must come from this value set if an appropriate concept is in the value set ")); + } + if (cols.contains("maximum")) { + columns.add(new Column("maximum", "Maximum", "A required binding for additional codes, for use when the binding strength is 'extensible' or 'preferred'")); + } + if (cols.contains("minimum")) { + columns.add(new Column("minimum", "Minimum", "The minimum allowable value set - any conformant system SHALL support all these codes")); + } + if (cols.contains("candidate")) { + columns.add(new Column("candidate", "Candidate", "This value set is a candidate to substitute for the overall conformance value set in some situations; usually these are defined in the documentation")); + } + if (cols.contains("current")) { + columns.add(new Column("current", "Current", "New records are required to use this value set, but legacy records may use other codes. The definition of 'new record' is difficult, since systems often create new records based on pre-existing data. Usually 'current' bindings are mandated by an external authority that makes clear rules around this")); + } + if (cols.contains("preferred")) { + columns.add(new Column("preferred", "Preferred", "This is the value set that is preferred in a given context (documentation should explain why)")); + } + if (cols.contains("ui")) { + columns.add(new Column("ui", "UI", "This value set is provided for user look up in a given context. Typically, these valuesets only include a subset of codes relevant for input in a context")); + } + if (cols.contains("starter")) { + columns.add(new Column("starter", "Starter", "This value set is a good set of codes to start with when designing your system")); + } + if (cols.contains("component")) { + columns.add(new Column("component", "Component", "This value set is a component of the base value set. Usually this is called out so that documentation can be written about a portion of the value set")); + } + if (cols.contains("example")) { + columns.add(new Column("example", "Example", "Instances are not expected or even encouraged to draw from the specified value set. The value set merely provides examples of the types of concepts intended to be included.")); + } + } + + public void scanBindings(Set cols, List list, ElementDefinition ed) { + if (ed.hasBinding()) { + if (ed.getBinding().hasValueSet()) { + switch (ed.getBinding().getStrength()) { + case EXAMPLE: + cols.add("example"); + break; + case EXTENSIBLE: + cols.add("extensible"); + break; + case PREFERRED: + cols.add("preferred"); + break; + case REQUIRED: + cols.add("required"); + break; + default: + break; + } + } + for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) { + cols.add(ab.getPurpose().toCode()); + } + for (Extension ext : ed.getBinding().getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { + cols.add(ext.getExtensionString("purpose")); + } + } + + List children = getChildren(list, ed); + for (ElementDefinition element : children) { + scanBindings(cols, list, element); + } + } + + private void scanObligations(List columns, List list) { + Set cols = new HashSet<>(); + scanObligations(cols, list, list.get(0)); + + if (cols.contains("$all")) { + columns.add(new Column("$all", "All Actors", "Obligations that apply to all actors")); + } + for (String col : cols) { + if (!"$all".equals(col)) { + ActorDefinition actor = context.getWorker().fetchResource(ActorDefinition.class, col); + if (actor == null) { + columns.add(new Column(col, tail(col), "Obligations that apply to the undefined actor "+col, col)); + } else { + columns.add(new Column(col, actor.getName(), "Obligations that apply to the actor "+actor.present(), actor.getUserString("path"))); + } + } + } + } + + private void scanObligations(Set cols, List list, ElementDefinition ed) { + + for (ElementDefinitionObligationComponent ob : ed.getObligation()) { + if (ob.hasActor()) { + for (CanonicalType a : ob.getActor()) { + cols.add(a.getValue()); + } + } else + cols.add("$all"); + } + + List children = getChildren(list, ed); + for (ElementDefinition element : children) { + scanObligations(cols, list, element); + } + } + + public TableModel initCustomTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, boolean alternating, String id, boolean isActive, List columns) { + TableModel model = gen.new TableModel(id, isActive); + + model.setAlternating(alternating); + model.setDocoImg(Utilities.pathURL(prefix, "help16.png")); + model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views")); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Name"), translate("sd.hint", "The logical name of the element"), null, 0)); + for (Column col : columns) { + model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", col.title), translate("sd.hint", col.hint), null, 0)); + } + return model; + } + private Row genElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, - boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, Resource srcSD) throws IOException, FHIRException { + boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, Resource srcSD, List columns) throws IOException, FHIRException { Row originalRow = slicingRow; StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); Row typesRow = null; @@ -375,7 +546,18 @@ public class StructureDefinitionRenderer extends ResourceRenderer { 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, all); - genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc); + switch (context.getStructureMode()) { + case BINDINGS: + genElementBindings(gen, element, columns, row, profile, corePath); + break; + case OBLIGATIONS: + genElementObligations(gen, element, columns, row); + break; + case SUMMARY: + genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc); + break; + + } 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(); @@ -408,10 +590,20 @@ public class StructureDefinitionRenderer extends ResourceRenderer { hrow.setLineColor(1); hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); hrow.getCells().add(gen.new Cell(null, null, sName+":All Slices", "", null)); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all slices", "", null)); + switch (context.getStructureMode()) { + case BINDINGS: + case OBLIGATIONS: + for (Column col : columns) { + hrow.getCells().add(gen.new Cell()); + } + break; + case SUMMARY: + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all slices", "", null)); + break; + } row.getSubRows().add(hrow); row = hrow; } @@ -423,10 +615,19 @@ public class StructureDefinitionRenderer extends ResourceRenderer { hrow.setLineColor(1); hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); hrow.getCells().add(gen.new Cell(null, null, sName+":All Types", "", null)); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell()); - hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all Types", "", null)); + switch (context.getStructureMode()) { + case BINDINGS: + case OBLIGATIONS: + for (Column col : columns) { + hrow.getCells().add(gen.new Cell()); + } + break; + case SUMMARY: + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell()); + hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all Types", "", null)); + } row.getSubRows().add(hrow); row = hrow; } @@ -442,7 +643,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { - currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix, srcSD); + currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix, srcSD, columns); } } } @@ -451,13 +652,169 @@ public class StructureDefinitionRenderer extends ResourceRenderer { // if (child.getPath().endsWith(".extension") || child.getPath().endsWith(".modifierExtension")) // genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); } - if (typesRow != null && !element.prohibited()) { + if (typesRow != null && !element.prohibited() && context.getStructureMode() == StructureDefinitionRendererMode.SUMMARY) { makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport, srcSD); } } return slicingRow; } + private void genElementObligations(HierarchicalTableGenerator gen, ElementDefinition element, List columns, Row row) throws IOException { + for (Column col : columns) { + Cell gc = gen.new Cell(); + row.getCells().add(gc); + List obligations = collectObligations(element, col.id); + if (obligations.size() > 0) { + Piece p = gen.new Piece(null); + gc.addPiece(p); + if (obligations.size() == 1) { + renderObligation(p.getChildren(), obligations.get(0)); + } else { + XhtmlNode ul = p.getChildren().ul(); + for (ElementDefinitionObligationComponent ob : obligations) { + renderObligation(ul.li().getChildNodes(), ob); + } + } + } + + } + } + + private List collectObligations(ElementDefinition element, String id) { + List res = new ArrayList<>(); + for (ElementDefinitionObligationComponent ob : element.getObligation()) { + if (("$all".equals(id) && !ob.hasActor()) || (ob.hasActor(id))) { + res.add(ob); + } + } + return res; + } + + private void renderObligation(XhtmlNodeList children, ElementDefinitionObligationComponent ob) throws IOException { + if ("http://hl7.org/fhir/tools/CodeSystem/obligation".equals(ob.getCode().getSystem())) { + boolean first = true; + String[] codes = ob.getCode().getCode().split("\\+"); + for (String code : codes) { + if (first) first = false; else children.tx(" & "); + int i = code.indexOf(":"); + if (i > -1) { + String c = code.substring(0, i); + code = code.substring(i+1); + children.b().tx(c.toUpperCase()); + children.tx(":"); + } + CodeResolution cr = resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code); + code = code.replace("will-", "").replace("can-", ""); + if (cr.getLink() != null) { + children.ah(cr.getLink(), cr.getHint()).tx(code); + } else { + children.span(null, cr.getHint()).tx(code); + } + } + + } else { + CodeResolution cr = resolveCode(ob.getCode()); + if (cr.getLink() != null) { + children.ah(cr.getLink(), cr.getHint()).tx(cr.getDisplay()); + } else { + children.span(null, cr.getHint()).tx(cr.getDisplay()); + } + } + if (ob.hasFilter() || ob.hasUsage()) { + children.tx(" ("); + boolean ffirst = !ob.hasFilter(); + if (ob.hasFilter()) { + children.span(null, ob.getFilterDocumentation()).code().tx(ob.getFilter()); + } + for (UsageContext uc : ob.getUsage()) { + if (ffirst) ffirst = false; else children.tx(","); + if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) { + children.tx(displayForUsage(uc.getCode())); + children.tx("="); + } + CodeResolution ccr = resolveCode(uc.getValueCodeableConcept()); + children.ah(ccr.getLink(), ccr.getHint()).tx(ccr.getDisplay()); + } + children.tx(")"); + } + // usage + // filter + // process + } + + + private String displayForUsage(Coding c) { + if (c.hasDisplay()) { + return c.getDisplay(); + } + if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) { + return c.getCode(); + } + return c.getCode(); + } + + private void genElementBindings(HierarchicalTableGenerator gen, ElementDefinition element, List columns, Row row, StructureDefinition profile, String corepath) { + for (Column col : columns) { + Cell gc = gen.new Cell(); + row.getCells().add(gc); + List bindings = collectBindings(element, col.id); + if (bindings.size() > 0) { + Piece p = gen.new Piece(null); + gc.addPiece(p); + new AdditionalBindingsRenderer(context.getPkp(), corepath, profile, element.getPath(), context, null, this).render(p.getChildren(), bindings); + } + } + } + + private List collectBindings(ElementDefinition element, String type) { + List res = new ArrayList<>(); + if (element.hasBinding()) { + ElementDefinitionBindingComponent b = element.getBinding(); + if (type.equals(b.getStrength().toCode())) { + ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); + res.add(ab.setAny(false).setDocumentation(b.getDescription()).setValueSet(b.getValueSet())); + } + if ("maximum".equals(type) && b.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { + ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); + res.add(ab.setAny(false).setValueSet(ToolingExtensions.readStringExtension(b, ToolingExtensions.EXT_MAX_VALUESET))); + } + if ("minimum".equals(type) && b.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) { + ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); + res.add(ab.setAny(false).setValueSet(ToolingExtensions.readStringExtension(b, ToolingExtensions.EXT_MIN_VALUESET))); + } + for (ElementDefinitionBindingAdditionalComponent t : b.getAdditional()) { + if (type.equals(t.getPurpose().toCode())) { + res.add(t); + } + } + for (Extension ext : b.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { + if (type.equals(ext.getExtensionString("purpose"))) { + ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); + if (ext.hasExtension("any")) { + ab.setAny(ToolingExtensions.readBooleanExtension(ext, "any")); + } + if (ext.hasExtension("purpose")) { + ab.setPurpose(AdditionalBindingPurposeVS.fromCode(ToolingExtensions.readStringExtension(ext, "purpose"))); + } + if (ext.hasExtension("documentation")) { + ab.setDocumentation(ToolingExtensions.readStringExtension(ext, "documentation")); + } + if (ext.hasExtension("shortDoco")) { + ab.setShortDoco(ToolingExtensions.readStringExtension(ext, "shortDoco")); + } + if (ToolingExtensions.hasExtension(ext, "usage")) { + ab.addUsage(ext.getExtensionByUrl("usage").getValueUsageContext()); + } + if (ext.hasExtension("valueSet")) { + ab.setValueSet(ToolingExtensions.readStringExtension(ext, "valueSet")); + } + res.add(ab); + } + } + } + return res; + } + public Cell genElementNameCell(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, List elements) throws IOException { @@ -592,7 +949,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return cell; } - private List supplementMissingDiffElements(StructureDefinition profile) { + public List supplementMissingDiffElements(StructureDefinition profile) { List list = new ArrayList<>(); list.addAll(profile.getDifferential().getElement()); if (list.isEmpty()) { @@ -694,11 +1051,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer { && 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, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { + public 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, RenderingContext rc) throws IOException, FHIRException { return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc); } - 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, RenderingContext rc) throws IOException, FHIRException { + public 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, RenderingContext rc) throws IOException, FHIRException { Cell c = gen.new Cell(); row.getCells().add(c); @@ -928,7 +1285,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()))); } - AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, profile, definition.getPath(), rc, null); + AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, profile, definition.getPath(), rc, null, this); if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)); } @@ -1223,7 +1580,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } - private static String codeForAggregation(AggregationMode a) { + public String codeForAggregation(AggregationMode a) { switch (a) { case BUNDLED : return "b"; case CONTAINED : return "c"; @@ -1232,7 +1589,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } - private static String hintForAggregation(AggregationMode a) { + public String hintForAggregation(AggregationMode a) { if (a != null) return a.getDefinition(); else @@ -1833,7 +2190,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return app == null ? src : src + app; } - private boolean hasNonBaseConditions(List conditions) { + public boolean hasNonBaseConditions(List conditions) { for (IdType c : conditions) { if (!isBaseCondition(c)) { return true; @@ -1843,7 +2200,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } - private boolean hasNonBaseConstraints(List constraints) { + public boolean hasNonBaseConstraints(List constraints) { for (ElementDefinitionConstraintComponent c : constraints) { if (!isBaseConstraint(c)) { return true; @@ -1852,7 +2209,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return false; } - private String listConstraintsAndConditions(ElementDefinition element) { + public String listConstraintsAndConditions(ElementDefinition element) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (ElementDefinitionConstraintComponent con : element.getConstraint()) { if (!isBaseConstraint(con)) { @@ -2036,7 +2393,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } - private static boolean allTypesMustSupport(ElementDefinition e) { + private boolean allTypesMustSupport(ElementDefinition e) { boolean all = true; boolean any = false; for (TypeRefComponent tr : e.getType()) { @@ -2046,7 +2403,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return !all && !any; } - private static boolean allProfilesMustSupport(List profiles) { + private boolean allProfilesMustSupport(List profiles) { boolean all = true; boolean any = false; for (CanonicalType u : profiles) { @@ -2055,11 +2412,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } return !all && !any; } - private static boolean isMustSupportDirect(TypeRefComponent tr) { + public boolean isMustSupportDirect(TypeRefComponent tr) { return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); } - private static boolean isMustSupport(TypeRefComponent tr) { + public boolean isMustSupport(TypeRefComponent tr) { if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) { return true; } @@ -2069,7 +2426,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return isMustSupport(tr.getTargetProfile()); } - private static boolean isMustSupport(List profiles) { + public boolean isMustSupport(List profiles) { for (CanonicalType ct : profiles) { if (isMustSupport(ct)) { return true; @@ -2079,7 +2436,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } - private static boolean isMustSupport(CanonicalType profile) { + public boolean isMustSupport(CanonicalType profile) { return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT)); } @@ -2278,5 +2635,30 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return ed.getPath().substring(ed.getPath().indexOf(".")+1); } + public static String formatTypeSpecifiers(IWorkerContext context, ElementDefinition d) { + StringBuilder b = new StringBuilder(); + boolean first = true; + for (Extension e : d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { + if (first) first = false; else b.append("
"); + String cond = ToolingExtensions.readStringExtension(e, "condition"); + String type = ToolingExtensions.readStringExtension(e, "type"); + b.append("If "); + b.append(Utilities.escapeXml(cond)); + b.append(" then the type is "); + StructureDefinition sd = context.fetchTypeDefinition(type); + if (sd == null) { + b.append(""); + b.append(Utilities.escapeXml(type)); + b.append(""); + } else { + b.append(""); + b.append(Utilities.escapeXml(sd.getTypeName())); + b.append(""); + } + } + return b.toString(); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java index 6fcb3e81f..46804dc84 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java @@ -74,6 +74,12 @@ public class RenderingContext { IG_PUBLISHER } + public enum StructureDefinitionRendererMode { + SUMMARY, // 5 cells: tree/name | flags | cardinality | type | details + BINDINGS, // tree/name + column for each kind of binding found, cells are lists of bindings + OBLIGATIONS, // tree/name + column for each actor that has obligations + } + public enum QuestionnaireRendererMode { /** * A visual presentation of the questionnaire, with a set of property panes that can be toggled on and off. @@ -139,6 +145,8 @@ public class RenderingContext { private boolean inlineGraphics; private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM; + private StructureDefinitionRendererMode structureMode = StructureDefinitionRendererMode.SUMMARY; + private boolean addGeneratedNarrativeHeader = true; private boolean showComments = false; @@ -201,6 +209,7 @@ public class RenderingContext { res.destDir = destDir; res.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader; res.questionnaireMode = questionnaireMode; + res.structureMode = structureMode; res.header = header; res.links.putAll(links); res.inlineGraphics = inlineGraphics; @@ -435,6 +444,15 @@ public class RenderingContext { this.questionnaireMode = questionnaireMode; return this; } + + public StructureDefinitionRendererMode getStructureMode() { + return structureMode; + } + + public RenderingContext setStructureMode(StructureDefinitionRendererMode structureMode) { + this.structureMode = structureMode; + return this; + } public String fixReference(String ref) { if (!Utilities.isAbsoluteUrl(ref)) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/JurisdictionUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/JurisdictionUtilities.java index 862312517..38dd154d3 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/JurisdictionUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/JurisdictionUtilities.java @@ -5996,5 +5996,9 @@ public class JurisdictionUtilities { } return "Unknown region code '"+c.getCode()+"'"; } + + public static boolean isJurisdiction(String system) { + return Utilities.existsInList(system, "http://unstats.un.org/unsd/methods/m49/m49.htm", "urn:iso:std:iso:3166", "urn:iso:std:iso:3166:-2"); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java index 448e4bb70..3ee6e683f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java @@ -39,7 +39,7 @@ public class TestPackageLoader implements IContextResourceLoader { @Override public String getResourcePath(Resource resource) { - return null; + return resource.fhirType().toLowerCase()+"-"+resource.getId()+".html"; } @Override diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java index a0cf7cd49..4e0594758 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java @@ -23,17 +23,22 @@ import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.renderers.RendererFactory; import org.hl7.fhir.r5.renderers.utils.ElementWrappers; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode; import org.hl7.fhir.r5.test.utils.CompareUtilities; +import org.hl7.fhir.r5.test.utils.TestPackageLoader; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xml.XMLUtil; @@ -87,6 +92,14 @@ public class NarrativeGenerationTests { @Override public BindingResolution resolveBinding(StructureDefinition def, String url, String path) throws FHIRException { + ValueSet vs = context.fetchResource(ValueSet.class, url); + if (vs != null) { + if (vs.hasUserData("path")) { + return new BindingResolution(vs.present(), vs.getUserString("path")); + } else { + return new BindingResolution(vs.present(), "valueset-"+vs.getIdBase()+".html"); + } + } throw new NotImplementedException(); } @@ -100,7 +113,7 @@ public class NarrativeGenerationTests { @Override public boolean prependLinks() { - throw new NotImplementedException(); + return false; } @Override @@ -138,13 +151,23 @@ public class NarrativeGenerationTests { public static class TestDetails { private String id; + private String sdmode; private boolean header; private boolean meta; private boolean technical; + private String register; public TestDetails(Element test) { super(); id = test.getAttribute("id"); + sdmode = test.getAttribute("sdmode"); + if ("".equals(sdmode)) { + sdmode = null; + } + register = test.getAttribute("register"); + if ("".equals(register)) { + register = null; + } header = "true".equals(test.getAttribute("header")); meta = "true".equals(test.getAttribute("meta")); technical = "technical".equals(test.getAttribute("mode")); @@ -154,12 +177,20 @@ public class NarrativeGenerationTests { return id; } + public String getSDMode() { + return sdmode; + } + public boolean isHeader() { return header; } public boolean isMeta() { return meta; + } + + public String getRegister() { + return register; } } @@ -177,13 +208,23 @@ public class NarrativeGenerationTests { } @BeforeAll - public static void setUp() { + public static void setUp() throws IOException { context = TestingUtilities.getSharedWorkerContext("5.0.0"); + FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true); + NpmPackage ips = pcm.loadPackage("hl7.fhir.uv.ips#1.1.0"); + context.loadFromPackage(ips, new TestPackageLoader(new String[] { "StructureDefinition", "ValueSet" })); } @ParameterizedTest(name = "{index}: file {0}") @MethodSource("data") public void test(String id, TestDetails test) throws Exception { + if (test.getRegister() != null) { + if (test.getRegister().endsWith(".json")) { + context.cacheResource(new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getRegister()))); + } else { + context.cacheResource(new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getRegister()))); + } + } RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.END_USER, GenerationRules.VALID_RESOURCE); rc.setDestDir(Utilities.path("[tmp]", "narrative")); rc.setHeader(test.isHeader()); @@ -199,6 +240,9 @@ public class NarrativeGenerationTests { rc.setMode(test.technical ? ResourceRendererMode.TECHNICAL : ResourceRendererMode.END_USER); rc.setProfileUtilities(new ProfileUtilities(rc.getContext(), null, new TestProfileKnowledgeProvider(rc.getContext()))); + if (test.getSDMode() != null) { + rc.setStructureMode(StructureDefinitionRendererMode.valueOf(test.getSDMode().toUpperCase())); + } Resource source; if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + ".json")) { @@ -209,7 +253,7 @@ public class NarrativeGenerationTests { XhtmlNode x = RendererFactory.factory(source, rc).build(source); String expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html")); - String actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER; + String actual = HEADER+new XhtmlComposer(true, false).compose(x)+FOOTER; String expectedFileName = CompareUtilities.tempFile("narrative", test.getId() + ".expected.html"); String actualFileName = CompareUtilities.tempFile("narrative", test.getId() + ".actual.html"); TextFile.stringToFile(expected, expectedFileName); diff --git a/org.hl7.fhir.r5/src/test/resources/txCache/org.hl7.fhir.r5/iso3166.cache b/org.hl7.fhir.r5/src/test/resources/txCache/org.hl7.fhir.r5/iso3166.cache new file mode 100644 index 000000000..17ea50a4b --- /dev/null +++ b/org.hl7.fhir.r5/src/test/resources/txCache/org.hl7.fhir.r5/iso3166.cache @@ -0,0 +1,11 @@ +------------------------------------------------------------------------------------- +{"code" : { + "system" : "urn:iso:std:iso:3166", + "code" : "BE" +}, "valueSet" :null, "lang":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"true"}#### +v: { + "severity" : "error", + "error" : "Local Error: Resolved system urn:iso:std:iso:3166 (v1.0.0), but the definition is not complete. Server Error: Attempt to use Terminology server when no Terminology server is available", + "class" : "SERVER_ERROR" +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java index 334464d7f..42a7e44f9 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java @@ -134,7 +134,7 @@ public class HierarchicalTableGenerator extends TranslatingUtilities { private String hint; private String style; private Map attributes; - private List children; + private XhtmlNodeList children; public Piece(String tag) { super(); @@ -205,9 +205,9 @@ public class HierarchicalTableGenerator extends TranslatingUtilities { return children != null && !children.isEmpty(); } - public List getChildren() { + public XhtmlNodeList getChildren() { if (children == null) - children = new ArrayList(); + children = new XhtmlNodeList(); return children; } @@ -853,8 +853,9 @@ public class HierarchicalTableGenerator extends TranslatingUtilities { } else if (p.getStyle() != null) { XhtmlNode s = addStyle(tc.addTag("span"), p); s.addText(p.getText()); - } else + } else { tc.addText(p.getText()); + } if (p.hasChildren()) { tc.getChildNodes().addAll(p.getChildren()); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlFluent.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlFluent.java new file mode 100644 index 000000000..93f1158d0 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlFluent.java @@ -0,0 +1,195 @@ +package org.hl7.fhir.utilities.xhtml; + +import java.io.IOException; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.utilities.MarkDownProcessor; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; + +public abstract class XhtmlFluent { + + protected abstract XhtmlNode addTag(String string); + protected abstract XhtmlNode addText(String cnt); + protected abstract void addChildren(XhtmlNodeList childNodes); + + public XhtmlNode h1() { + return addTag("h1"); + } + + + public XhtmlNode h2() { + return addTag("h2"); + } + + public XhtmlNode h(int level) { + if (level < 1 || level > 6) { + throw new FHIRException("Illegal Header level "+level); + } + return addTag("h"+Integer.toString(level)); + } + + public XhtmlNode h3() { + return addTag("h3"); + } + + public XhtmlNode h4() { + return addTag("h4"); + } + + public XhtmlNode table(String clss) { + XhtmlNode res = addTag("table"); + if (!Utilities.noString(clss)) + res.setAttribute("class", clss); + return res; + } + + public XhtmlNode tr() { + return addTag("tr"); + } + + public XhtmlNode th() { + return addTag("th"); + } + + public XhtmlNode td() { + return addTag("td"); + } + + public XhtmlNode td(String clss) { + return addTag("td").attribute("class", clss); + } + + public XhtmlNode div() { + return addTag("div"); + } + + public XhtmlNode para() { + return addTag("p"); + } + + public XhtmlNode pre() { + return addTag("pre"); + } + + public XhtmlNode pre(String clss) { + return addTag("pre").setAttribute("class", clss); + } + + public void br() { + addTag("br"); + } + + public void hr() { + addTag("hr"); + } + + public XhtmlNode ul() { + return addTag("ul"); + } + + public XhtmlNode li() { + return addTag("li"); + } + + public XhtmlNode b() { + return addTag("b"); + } + + public XhtmlNode i() { + return addTag("i"); + } + + public XhtmlNode tx(String cnt) { + return addText(cnt); + } + + public XhtmlNode tx(int cnt) { + return addText(Integer.toString(cnt)); + } + + public XhtmlNode ah(String href) { + return addTag("a").attribute("href", href); + } + + public XhtmlNode ah(String href, String title) { + XhtmlNode x = addTag("a").attribute("href", href); + if (title != null) { + x.attribute("title", title); + } + return x; + } + + public XhtmlNode img(String src, String alt) { + return addTag("img").attribute("src", src).attribute("alt", alt); + } + + public XhtmlNode img(String src, String alt, String title) { + return addTag("img").attribute("src", src).attribute("alt", alt).attribute("title", title); + } + + public XhtmlNode an(String href) { + return an(href, " "); + } + + public XhtmlNode an(String href, String tx) { + XhtmlNode a = addTag("a").attribute("name", href); + a.tx(tx); + return a; + } + + public XhtmlNode span(String style, String title) { + XhtmlNode res = addTag("span"); + if (!Utilities.noString(style)) + res.attribute("style", style); + if (!Utilities.noString(title)) + res.attribute("title", title); + return res; + } + + + public XhtmlNode code(String text) { + return addTag("code").tx(text); + } + + public XhtmlNode code() { + return addTag("code"); + } + + + public XhtmlNode blockquote() { + return addTag("blockquote"); + } + + + public void markdown(String md, String source) throws IOException { + if (md != null) { + String s = new MarkDownProcessor(Dialect.COMMON_MARK).process(md, source); + XhtmlParser p = new XhtmlParser(); + XhtmlNode m; + try { + m = p.parse("
"+s+"
", "div"); + } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { + throw new FHIRFormatError(e.getMessage(), e); + } + addChildren(m.getChildNodes()); + } + } + + public void innerHTML(String html) throws IOException { + if (html != null) { + XhtmlParser p = new XhtmlParser(); + XhtmlNode m; + try { + m = p.parse("
"+html+"
", "div"); + } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { + throw new FHIRFormatError(e.getMessage(), e); + } + addChildren(m.getChildNodes()); + } + } + + + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index 0869f1fb3..2bccd0e3a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -35,7 +35,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.Serializable; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,7 +49,7 @@ import org.hl7.fhir.utilities.Utilities; import ca.uhn.fhir.model.primitive.XhtmlDt; @ca.uhn.fhir.model.api.annotation.DatatypeDef(name="xhtml") -public class XhtmlNode implements IBaseXhtml { +public class XhtmlNode extends XhtmlFluent implements IBaseXhtml { private static final long serialVersionUID = -4362547161441436492L; @@ -83,11 +82,9 @@ public class XhtmlNode implements IBaseXhtml { private NodeType nodeType; private String name; private Map attributes = new HashMap(); - private List childNodes = new ArrayList(); + private XhtmlNodeList childNodes = new XhtmlNodeList(); private String content; private boolean notPretty; - private boolean inPara; - private boolean inLink; private boolean seperated; private Boolean emptyExpanded; @@ -129,7 +126,7 @@ public class XhtmlNode implements IBaseXhtml { return attributes; } - public List getChildNodes() { + public XhtmlNodeList getChildNodes() { return childNodes; } @@ -205,11 +202,11 @@ public class XhtmlNode implements IBaseXhtml { // } XhtmlNode node = new XhtmlNode(NodeType.Element); node.setName(name); - if (inPara || name.equals("p")) { - node.inPara = true; + if (childNodes.isInPara() || name.equals("p")) { + node.getChildNodes().setInPara(true); } - if (inLink || name.equals("a")) { - node.inLink = true; + if (childNodes.isInLink() || name.equals("a")) { + node.getChildNodes().setInLink(true); } childNodes.add(node); return node; @@ -221,11 +218,11 @@ public class XhtmlNode implements IBaseXhtml { if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) throw new Error("Wrong node type. is "+nodeType.toString()); XhtmlNode node = new XhtmlNode(NodeType.Element); - if (inPara || name.equals("p")) { - node.inPara = true; + if (childNodes.isInPara() || name.equals("p")) { + node.getChildNodes().setInPara(true); } - if (inLink || name.equals("a")) { - node.inLink = true; + if (childNodes.isInLink() || name.equals("a")) { + node.getChildNodes().setInLink(true); } node.setName(name); childNodes.add(index, node); @@ -554,159 +551,7 @@ public class XhtmlNode implements IBaseXhtml { } // xhtml easy adders ----------------------------------------------- - public XhtmlNode h1() { - return addTag("h1"); - } - public XhtmlNode h2() { - return addTag("h2"); - } - - public XhtmlNode h(int level) { - if (level < 1 || level > 6) { - throw new FHIRException("Illegal Header level "+level); - } - return addTag("h"+Integer.toString(level)); - } - - public XhtmlNode h3() { - return addTag("h3"); - } - - public XhtmlNode h4() { - return addTag("h4"); - } - - public XhtmlNode table(String clss) { - XhtmlNode res = addTag("table"); - if (!Utilities.noString(clss)) - res.setAttribute("class", clss); - return res; - } - - public XhtmlNode tr() { - return addTag("tr"); - } - - public XhtmlNode th() { - return addTag("th"); - } - - public XhtmlNode td() { - return addTag("td"); - } - - public XhtmlNode td(String clss) { - return addTag("td").attribute("class", clss); - } - - public XhtmlNode colspan(String n) { - return setAttribute("colspan", n); - } - - public XhtmlNode div() { - return addTag("div"); - } - - public XhtmlNode para() { - return addTag("p"); - } - - public XhtmlNode pre() { - return addTag("pre"); - } - - public XhtmlNode pre(String clss) { - return addTag("pre").setAttribute("class", clss); - } - - public void br() { - addTag("br"); - } - - public void hr() { - addTag("hr"); - } - - public XhtmlNode ul() { - return addTag("ul"); - } - - public XhtmlNode li() { - return addTag("li"); - } - - public XhtmlNode b() { - return addTag("b"); - } - - public XhtmlNode i() { - return addTag("i"); - } - - public XhtmlNode tx(String cnt) { - return addText(cnt); - } - - // differs from tx because it returns the owner node, not the created text - public XhtmlNode txN(String cnt) { - addText(cnt); - return this; - } - - public XhtmlNode tx(int cnt) { - return addText(Integer.toString(cnt)); - } - - public XhtmlNode ah(String href) { - return addTag("a").attribute("href", href); - } - - public XhtmlNode ah(String href, String title) { - return addTag("a").attribute("href", href).attribute("title", title); - } - - public XhtmlNode img(String src, String alt) { - return addTag("img").attribute("src", src).attribute("alt", alt); - } - - public XhtmlNode img(String src, String alt, String title) { - return addTag("img").attribute("src", src).attribute("alt", alt).attribute("title", title); - } - - public XhtmlNode an(String href) { - return an(href, " "); - } - - public XhtmlNode an(String href, String tx) { - XhtmlNode a = addTag("a").attribute("name", href); - a.tx(tx); - return a; - } - - public XhtmlNode span(String style, String title) { - XhtmlNode res = addTag("span"); - if (!Utilities.noString(style)) - res.attribute("style", style); - if (!Utilities.noString(title)) - res.attribute("title", title); - return res; - } - - - public XhtmlNode code(String text) { - return addTag("code").tx(text); - } - - public XhtmlNode code() { - return addTag("code"); - } - - - public XhtmlNode blockquote() { - return addTag("blockquote"); - } - @Override public String toString() { @@ -847,36 +692,6 @@ public class XhtmlNode implements IBaseXhtml { return "p".equals(name); } - - public void markdown(String md, String source) throws IOException { - if (md != null) { - String s = new MarkDownProcessor(Dialect.COMMON_MARK).process(md, source); - XhtmlParser p = new XhtmlParser(); - XhtmlNode m; - try { - m = p.parse("
"+s+"
", "div"); - } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { - throw new FHIRFormatError(e.getMessage(), e); - } - getChildNodes().addAll(m.getChildNodes()); - } - } - - public void innerHTML(String html) throws IOException { - if (html != null) { - XhtmlParser p = new XhtmlParser(); - XhtmlNode m; - try { - m = p.parse("
"+html+"
", "div"); - } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { - throw new FHIRFormatError(e.getMessage(), e); - } - getChildNodes().addAll(m.getChildNodes()); - } - } - - - public XhtmlNode sep(String separator) { // if there's already text, add the separator. otherwise, we'll add it next time if (!seperated) { @@ -887,8 +702,23 @@ public class XhtmlNode implements IBaseXhtml { } - + // more fluent + public XhtmlNode colspan(String n) { + return setAttribute("colspan", n); + } + + // differs from tx because it returns the owner node, not the created text + public XhtmlNode txN(String cnt) { + addText(cnt); + return this; + } + + + @Override + protected void addChildren(XhtmlNodeList childNodes) { + this.childNodes.addAll(childNodes); + } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java new file mode 100644 index 000000000..4cc1d8736 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java @@ -0,0 +1,185 @@ +package org.hl7.fhir.utilities.xhtml; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +public class XhtmlNodeList extends XhtmlFluent implements List { + + private List list = new ArrayList<>(); + private boolean inPara; + private boolean inLink; + + + public boolean isInPara() { + return inPara; + } + + public void setInPara(boolean inPara) { + this.inPara = inPara; + } + + public boolean isInLink() { + return inLink; + } + + public void setInLink(boolean inLink) { + this.inLink = inLink; + } + + + public XhtmlNode addTag(String name) + { + +// if (inPara && name.equals("p")) { +// throw new FHIRException("nested Para"); +// } +// if (inLink && name.equals("a")) { +// throw new FHIRException("Nested Link"); +// } + XhtmlNode node = new XhtmlNode(NodeType.Element); + node.setName(name); + if (isInPara() || name.equals("p")) { + node.getChildNodes().setInPara(true); + } + if (isInLink() || name.equals("a")) { + node.getChildNodes().setInLink(true); + } + add(node); + return node; + } + + public XhtmlNode addText(String content) { + if (content != null) { + XhtmlNode node = new XhtmlNode(NodeType.Text); + node.setContent(content); + add(node); + return node; + } else { + return null; + } + } + + @Override + public int size() { + return list.size(); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return list.contains(o); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return list.toArray(a); + } + + @Override + public boolean add(XhtmlNode e) { + return list.add(e); + } + + @Override + public boolean remove(Object o) { + return list.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return list.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return list.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + return list.addAll(index, c); + } + + @Override + public boolean removeAll(Collection c) { + return list.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return list.retainAll(c); + } + + @Override + public void clear() { + list.clear(); + } + + @Override + public XhtmlNode get(int index) { + return list.get(index); + } + + @Override + public XhtmlNode set(int index, XhtmlNode element) { + return list.set(index, element); + } + + @Override + public void add(int index, XhtmlNode element) { + list.add(index, element); + } + + @Override + public XhtmlNode remove(int index) { + return list.remove(index); + } + + @Override + public int indexOf(Object o) { + return list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return list.lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + return list.listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return list.listIterator(index); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return list.subList(fromIndex, toIndex); + } + + + @Override + protected void addChildren(XhtmlNodeList childNodes) { + this.addAll(childNodes); + } +} \ No newline at end of file From db48892b666804d5bc5231e4ed9928a9ae722b17 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Jan 2023 14:57:08 +1100 Subject: [PATCH 4/4] fix failing test --- .../main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java index 4cc1d8736..30ba2f1c3 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNodeList.java @@ -6,8 +6,9 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; -public class XhtmlNodeList extends XhtmlFluent implements List { +public class XhtmlNodeList extends XhtmlFluent implements List, java.io.Serializable { + private static final long serialVersionUID = 1L; private List list = new ArrayList<>(); private boolean inPara; private boolean inLink;