From 63767a78c3a9dfacae27bc2ecac2e39ad309ffbf Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 28 Oct 2022 17:49:22 +1100 Subject: [PATCH] more work in CDS Hooks extensions --- .../fhir/r5/conformance/ProfileUtilities.java | 56 ++++++++++++++ .../fhir/r5/context/SimpleWorkerContext.java | 5 +- .../fhir/r5/renderers/CodeSystemRenderer.java | 3 +- .../OperationDefinitionRenderer.java | 3 +- .../r5/renderers/QuestionnaireRenderer.java | 23 +++--- .../QuestionnaireResponseRenderer.java | 15 ++-- .../r5/renderers/SearchParameterRenderer.java | 3 +- .../StructureDefinitionRenderer.java | 3 +- .../r5/renderers/utils/RenderingContext.java | 43 ++++++----- .../fhir/r5/test/utils/TestingUtilities.java | 2 + .../java/org/hl7/fhir/r5/utils/R5Hacker.java | 74 +++++++++++++++++++ .../hl7/fhir/r5/utils/ToolingExtensions.java | 27 ++++++- 12 files changed, 213 insertions(+), 44 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/R5Hacker.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 88a4da1ec..fb192456a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -117,6 +117,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.renderers.TerminologyRenderer; import org.hl7.fhir.r5.renderers.spreadsheets.SpreadsheetGenerator; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.FHIRLexer; import org.hl7.fhir.r5.utils.FHIRPathEngine; @@ -723,6 +724,13 @@ public class ProfileUtilities extends TranslatingUtilities { ElementDefinition outcome = updateURLs(url, webUrl, e.copy()); e.setUserData(GENERATED_IN_SNAPSHOT, outcome); derived.getSnapshot().addElement(outcome); + if (walksInto(diff.getElement(), e)) { + if (e.getType().size() > 1) { + throw new DefinitionException("Unsupported scenario: specialization walks into multiple types at "+e.getId()); + } else { + addInheritedElementsForSpecialization(derived.getSnapshot(), outcome, outcome.getTypeFirstRep().getWorkingCode(), outcome.getPath(), url, webUrl); + } + } } } } @@ -851,6 +859,28 @@ public class ProfileUtilities extends TranslatingUtilities { } } + + private void addInheritedElementsForSpecialization(StructureDefinitionSnapshotComponent snapshot, ElementDefinition focus, String type, String path, String url, String weburl) { + StructureDefinition sd = context.fetchTypeDefinition(type); + if (sd != null) { + addInheritedElementsForSpecialization(snapshot, focus, sd.getBaseDefinition(), path, url, weburl); + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().contains(".")) { + ElementDefinition outcome = updateURLs(url, weburl, ed.copy()); + outcome.setPath(outcome.getPath().replace(sd.getType(), path)); + snapshot.getElement().add(outcome); + } else { + focus.getConstraint().addAll(ed.getConstraint()); + } + } + } + } + + private boolean walksInto(List list, ElementDefinition ed) { + int i = list.indexOf(ed); + return (i < list.size() - 1) && list.get(i + 1).getPath().startsWith(ed.getPath()+"."); + } + private void fixTypeOfResourceId(StructureDefinition base) { if (base.getKind() == StructureDefinitionKind.RESOURCE && (base.getFhirVersion() == null || VersionUtilities.isR4Plus(base.getFhirVersion().toCode()))) { fixTypeOfResourceId(base.getSnapshot().getElement()); @@ -4901,6 +4931,18 @@ public class ProfileUtilities extends TranslatingUtilities { 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")) { @@ -4940,6 +4982,20 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(gen.new Piece(null, "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_NULLABLE)) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(gen.new Piece(null, "This object can be represented as null in the JSON structure (which counts as 'present' for cardinality purposes)", null)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index bc3996390..0efac0a70 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -71,6 +71,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; import org.hl7.fhir.r5.terminologies.TerminologyClient; import org.hl7.fhir.r5.utils.validation.IResourceValidator; +import org.hl7.fhir.r5.utils.R5Hacker; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.CSFileInputStream; import org.hl7.fhir.utilities.TextFile; @@ -112,9 +113,9 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon FileInputStream f = new FileInputStream(filename); try { if (loader != null) { - return (CanonicalResource) loader.loadResource(f, true); + return R5Hacker.fixR5BrokenResource((CanonicalResource) loader.loadResource(f, true)); } else { - return (CanonicalResource) new JsonParser().parse(f); + return R5Hacker.fixR5BrokenResource((CanonicalResource) new JsonParser().parse(f)); } } finally { f.close(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java index d31982eaf..9d99a600c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java @@ -20,6 +20,7 @@ import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator; @@ -468,7 +469,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { first = false; XhtmlNode span = td.span(null, mapping.comp.hasRelationship() ? mapping.comp.getRelationship().toCode() : ""); span.addText(getCharForRelationship(mapping.comp)); - a = td.ah(getContext().getSpecificationLink()+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode())); + a = td.ah(getContext().getLink(KnownLinkType.SPEC)+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode())); a.addText(mapping.comp.getCode()); if (!Utilities.noString(mapping.comp.getComment())) td.i().tx("("+mapping.comp.getComment()+")"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java index 5cd96699c..644aee8c9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationDefinitionRenderer.java @@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterCom import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -123,7 +124,7 @@ public class OperationDefinitionRenderer extends TerminologyRenderer { if (p.hasSearchType()) { td.br(); td.tx("("); - td.ah( context.getSpecificationLink() == null ? "search.html#"+p.getSearchType().toCode() : Utilities.pathURL(context.getSpecificationLink(), "search.html#"+p.getSearchType().toCode())).tx(p.getSearchType().toCode()); + td.ah( context.getLink(KnownLinkType.SPEC) == null ? "search.html#"+p.getSearchType().toCode() : Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "search.html#"+p.getSearchType().toCode())).tx(p.getSearchType().toCode()); td.tx(")"); } td = tr.td(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java index bfbbeed75..ce696f24a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java @@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.Utilities; @@ -68,8 +69,8 @@ public class QuestionnaireRenderer extends TerminologyRenderer { HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true); TableModel model = gen.new TableModel("qtree="+q.getId(), !forResource); model.setAlternating(true); - model.setDocoImg(context.getSpecificationLink() +"help16.png"); - model.setDocoRef(context.getSpecificationLink()+"formats.html#table"); + model.setDocoImg(context.getLink(KnownLinkType.SPEC) +"help16.png"); + model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Text"), translate("sd.hint", "Text for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Cardinality"), translate("sd.hint", "Minimum and Maximum # of times the the itemcan appear in the instance"), null, 0)); @@ -131,7 +132,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { li.tx(opt.getValue().primitiveValue()); } else if (opt.getValue() instanceof Coding) { Coding c = (Coding) opt.getValue(); - String link = c.hasSystem() ? new ContextUtilities(context.getWorker()).getLinkForUrl(context.getSpecificationLink(), c.getSystem()) : null; + String link = c.hasSystem() ? new ContextUtilities(context.getWorker()).getLinkForUrl(context.getLink(KnownLinkType.SPEC), c.getSystem()) : null; if (link == null) { li.tx(c.getSystem()+"#"+c.getCode()); } else { @@ -213,7 +214,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { } private String getSpecLink(String path) { - return Utilities.pathURL(context.getSpecificationLink(), path); + return Utilities.pathURL(context.getLink(KnownLinkType.SPEC), path); } private String getSDCLink(String path) { @@ -234,9 +235,9 @@ public class QuestionnaireRenderer extends TerminologyRenderer { r.getCells().add(gen.new Cell(null, null, (i.getRequired() ? "1" : "0")+".."+(i.getRepeats() ? "*" : "1"), null, null)); if (i.getTypeElement().hasExtension(EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL)) { String t = i.getTypeElement().getExtensionString(EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL); - r.getCells().add(gen.new Cell(null, context.getSpecificationLink()+"codesystem-item-type.html#item-type-"+t, t, null, null)); + r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+t, t, null, null)); } else { - r.getCells().add(gen.new Cell(null, context.getSpecificationLink()+"codesystem-item-type.html#item-type-"+i.getType().toCode(), i.getType().toCode(), null, null)); + r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+i.getType().toCode(), i.getType().toCode(), null, null)); } if (hasFlags) { @@ -244,7 +245,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { Cell flags = gen.new Cell(); r.getCells().add(flags); if (i.getReadOnly()) { - flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-readonly.png")))); + flags.addPiece(gen.new Piece(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-readonly.png")))); } if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) { flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png")))); @@ -433,8 +434,8 @@ public class QuestionnaireRenderer extends TerminologyRenderer { HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true); TableModel model = gen.new TableModel("qtree="+q.getId(), true); model.setAlternating(true); - model.setDocoImg(context.getSpecificationLink() +"help16.png"); - model.setDocoRef(context.getSpecificationLink()+"formats.html#table"); + model.setDocoImg(context.getLink(KnownLinkType.SPEC) +"help16.png"); + model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Description & Constraints"), translate("sd.hint", "Additional information about the item"), null, 0)); @@ -683,7 +684,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { } if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_HIDDEN)) { hasFlag = true; - flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"), "icon"); + flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"), "icon"); d.style("background-color: #eeeeee"); } if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_OTP_DISP)) { @@ -836,7 +837,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { private boolean renderLinks(XhtmlNode x, Questionnaire q) { x.para().tx("Try this questionnaire out:"); XhtmlNode ul = x.ul(); - ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getSelfLink(), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); + ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); return false; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java index b5c679323..7c9c7f001 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java @@ -15,6 +15,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; 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.KnownLinkType; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; @@ -62,8 +63,8 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true); TableModel model = gen.new TableModel("qtree="+qr.getId(), false); model.setAlternating(true); - model.setDocoImg(context.getSpecificationLink() +"help16.png"); - model.setDocoRef(context.getSpecificationLink()+"formats.html#table"); + model.setDocoImg(context.getLink(KnownLinkType.SPEC) +"help16.png"); + model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Text"), translate("sd.hint", "Text for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Definition"), translate("sd.hint", "Minimum and Maximum # of times the the itemcan appear in the instance"), null, 0)); @@ -85,8 +86,8 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context.getDestDir(), context.isInlineGraphics(), true); TableModel model = gen.new TableModel("qtree="+q.getId(), true); model.setAlternating(true); - model.setDocoImg(context.getSpecificationLink() +"help16.png"); - model.setDocoRef(context.getSpecificationLink()+"formats.html#table"); + model.setDocoImg(context.getLink(KnownLinkType.SPEC) +"help16.png"); + model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "LinkId"), translate("sd.hint", "The linkId for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Text"), translate("sd.hint", "Text for the item"), null, 0)); model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Definition"), translate("sd.hint", "Minimum and Maximum # of times the the itemcan appear in the instance"), null, 0)); @@ -432,7 +433,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // } // if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-hidden")) { // hasFlag = true; -// flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-QuestionnaireResponse-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png")); +// flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-QuestionnaireResponse-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png")); // d.style("background-color: #eeeeee"); // } // if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-optionalDisplay")) { @@ -584,14 +585,14 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { private boolean renderLinks(XhtmlNode x, QuestionnaireResponse q) { x.para().tx("Try this QuestionnaireResponse out:"); XhtmlNode ul = x.ul(); - ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getSelfLink(), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); + ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); return false; } private boolean renderLinks(XhtmlNode x, ResourceWrapper q) { x.para().tx("Try this QuestionnaireResponse out:"); XhtmlNode ul = x.ul(); - ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getSelfLink(), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); + ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx("NLM Forms Library"); return false; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java index 4d98241a6..446a1c1af 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java @@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.utilities.Utilities; @@ -73,7 +74,7 @@ public class SearchParameterRenderer extends TerminologyRenderer { tr.td().tx(Utilities.pluralize("Target Resources", spd.getTarget().size())); td = tr.td(); if (isAllConcreteResources(spd.getTarget())) { - td.ah(Utilities.pathURL(context.getSpecificationLink(), "resourcelist.html")).tx("All Resources"); + td.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "resourcelist.html")).tx("All Resources"); } else { for (CodeType t : spd.getTarget()) { StructureDefinition sd = context.getWorker().fetchTypeDefinition(t.toString()); 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 97b3a63c8..dfdfba03f 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 @@ -10,6 +10,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 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.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.utilities.xhtml.XhtmlNode; @@ -28,7 +29,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, context.getSpecificationLink(), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, false, context, "")); + x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getLink(KnownLinkType.SPEC), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, false, context, "")); return true; } 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 ab59b006d..8384312d8 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 @@ -6,8 +6,10 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.TimeZone; import org.hl7.fhir.exceptions.FHIRException; @@ -91,6 +93,12 @@ public class RenderingContext { LINKS } + public enum KnownLinkType { + SELF, // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools) + SPEC, // version specific link to core specification + JSON_NAMES + + } private IWorkerContext worker; private MarkDownProcessor markdown; private ResourceRendererMode mode; @@ -101,8 +109,6 @@ public class RenderingContext { private String lang; private String localPrefix; // relative link within local context - private String specificationLink; - private String selfLink; // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools) private int headerLevelContext; private boolean canonicalUrlsAsLinks; private boolean pretty; @@ -136,6 +142,7 @@ public class RenderingContext { private boolean copyButton; private ProfileKnowledgeProvider pkp; + private Map links = new HashMap<>(); /** * * @param context - access to all related resources that might be needed @@ -149,7 +156,7 @@ public class RenderingContext { this.worker = worker; this.markdown = markdown; this.lang = lang; - this.specificationLink = specLink; + this.links.put(KnownLinkType.SPEC, specLink); this.localPrefix = localPrefix; this.mode = mode; if (terminologyServiceOptions != null) { @@ -159,7 +166,7 @@ public class RenderingContext { this.locale = new Locale.Builder().setLanguageTag("en-US").build(); } public RenderingContext copy() { - RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, specificationLink, localPrefix, lang, mode); + RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, getLink(KnownLinkType.SPEC), localPrefix, lang, mode); res.resolver = resolver; res.templateProvider = templateProvider; @@ -184,7 +191,7 @@ public class RenderingContext { res.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader; res.questionnaireMode = questionnaireMode; res.header = header; - res.selfLink = selfLink; + res.links.putAll(links); res.inlineGraphics = inlineGraphics; res.timeZoneId = timeZoneId; res.dateTimeFormat = dateTimeFormat; @@ -241,10 +248,6 @@ public class RenderingContext { return lang; } - public String getSpecificationLink() { - return specificationLink; - } - public String getLocalPrefix() { return localPrefix; } @@ -419,21 +422,12 @@ public class RenderingContext { return this; } - public String getSelfLink() { - return selfLink; - } - - public RenderingContext setSelfLink(String selfLink) { - this.selfLink = selfLink; - return this; - } - public String fixReference(String ref) { if (!Utilities.isAbsoluteUrl(ref)) { return (localPrefix == null ? "" : localPrefix)+ref; } if (ref.startsWith("http://hl7.org/fhir") && !ref.substring(20).contains("/")) { - return specificationLink+ref.substring(20); + return getLink(KnownLinkType.SPEC)+ref.substring(20); } return ref; } @@ -617,5 +611,16 @@ public class RenderingContext { return pkp; } + public boolean hasLink(KnownLinkType link) { + return links.containsKey(link); + } + public String getLink(KnownLinkType link) { + return links.get(link); + } + public void addLink(KnownLinkType self, String targetOutput) { + // TODO Auto-generated method stub + + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java index 8e034338d..08b3ead30 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java @@ -19,6 +19,7 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.context.TerminologyCache; import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.utils.R5Hacker; import org.hl7.fhir.utilities.CSFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.ToolGlobalSettings; @@ -117,6 +118,7 @@ public class TestingUtilities extends BaseTestingUtilities { System.out.println("Loading THO: "+utg.name()+"#"+utg.version()); fcontext.loadFromPackage(utg, new TestPackageLoader(new String[]{"CodeSystem", "ValueSet"})); } + R5Hacker.fixR5BrokenResources(fcontext); return fcontext; } catch (Exception e) { e.printStackTrace(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/R5Hacker.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/R5Hacker.java new file mode 100644 index 000000000..7db230057 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/R5Hacker.java @@ -0,0 +1,74 @@ +package org.hl7.fhir.r5.utils; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.Enumerations.BindingStrength; + +public class R5Hacker { + + public static void fixR5BrokenResources(IWorkerContext context) { + for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) { + fixSD(sd); + } + } + + + private static void fixSD(StructureDefinition sd) { + if ("5.0.0-ballot".equals(sd.getVersion()) && "ElementDefinition".equals(sd.getType())) { + for (ElementDefinition ed : sd.getDifferential().getElement()) { + hackEDR5BallotError(ed); + } + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + hackEDR5BallotError(ed); + } + } + if ("5.0.0-ballot".equals(sd.getVersion()) && "Base".equals(sd.getType())) { + for (ElementDefinition ed : sd.getDifferential().getElement()) { + hackBaseR5BallotError(ed); + } + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + hackBaseR5BallotError(ed); + } + } + if ("5.0.0-ballot".equals(sd.getVersion()) && "Bundle".equals(sd.getType())) { + for (ElementDefinition ed : sd.getDifferential().getElement()) { + hackBundleR5BallotError(ed); + } + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + hackBundleR5BallotError(ed); + } + } + if ("5.0.0-ballot".equals(sd.getVersion()) && "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype".equals(sd.getUrl())) { + sd.getContextFirstRep().setExpression("ElementDefinition"); + } + } + + + private static void hackBaseR5BallotError(ElementDefinition ed) { + ed.getConstraint().clear(); + } + + private static void hackBundleR5BallotError(ElementDefinition ed) { + if (ed.getPath().equals("Bundle.link.relation")) { + ToolingExtensions.removeExtension(ed.getBinding(), ToolingExtensions.EXT_BINDING_NAME); + } + } + + private static void hackEDR5BallotError(ElementDefinition ed) { + if (ed.getPath().equals("ElementDefinition.type.code")) { + ed.getBinding().setStrength(BindingStrength.EXTENSIBLE); + } + } + + + public static CanonicalResource fixR5BrokenResource(CanonicalResource cr) { + if (cr instanceof StructureDefinition) { + StructureDefinition sd = (StructureDefinition) cr; + fixSD(sd); + } + return cr; + } + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index 71de59075..c9c44a34a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -119,7 +119,6 @@ public class ToolingExtensions { public static final String EXT_RESOURCE_IMPLEMENTS = "http://hl7.org/fhir/StructureDefinition/structuredefinition-implements"; public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type"; public static final String EXT_XML_NAME = "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name"; - public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-style"; public static final String EXT_EXPLICIT_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"; public static final String EXT_IGP_RESOURCES = "http://hl7.org/fhir/StructureDefinition/igpublisher-folder-resource"; @@ -138,6 +137,9 @@ public class ToolingExtensions { public static final String EXT_IGP_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/resource-information"; public static final String EXT_IGP_LOADVERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-loadversion"; public static final String EXT_LIST_PACKAGE = "http://hl7.org/fhir/StructureDefinition/list-packageId"; + public static final String EXT_JSON_NAME = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-json-name"; + public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-binding-style"; + public static final String EXT_EXTENSION_STYLE = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-extension-style"; // validated // private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid"; @@ -233,6 +235,11 @@ public class ToolingExtensions { public static final String EXT_MAPPING_TGTTYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-type"; public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality"; + + + public static final String WEB_EXTENSION_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/branches/master/format-extensions.html#extension-related-extensions"; + ; + // specific extension helpers public static Extension makeIssueSource(Source source) { @@ -989,5 +996,23 @@ public class ToolingExtensions { } + public static boolean hasExtensions(ElementDefinition d, String... urls) { + for (String url : urls) { + if (d.hasExtension(url)) { + return true; + } + } + return false; + } + + public static int countExtensions(ElementDefinition d, String... urls) { + int res = 0; + for (String url : urls) { + if (d.hasExtension(url)) { + res++; + } + } + return res; + } } \ No newline at end of file