From a53ece85f19b7259167c5ae95f0afd29e935a700 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Jan 2025 14:08:01 +1100 Subject: [PATCH 01/13] Accessibility - role=presentation on appropriate tables --- .../hl7/fhir/r5/comparison/ResourceComparer.java | 2 +- .../java/org/hl7/fhir/r5/ips/IPSBuilder.java | 4 ++-- .../java/org/hl7/fhir/r5/ips/IPSRenderer.java | 2 +- .../r5/renderers/ActorDefinitionRenderer.java | 2 +- .../renderers/CapabilityStatementRenderer.java | 16 ++++++++-------- .../fhir/r5/renderers/CodeSystemRenderer.java | 8 ++++---- .../fhir/r5/renderers/ConceptMapRenderer.java | 6 +++--- .../r5/renderers/DiagnosticReportRenderer.java | 4 ++-- .../r5/renderers/ExampleScenarioRenderer.java | 6 +++--- .../r5/renderers/FeatureDefinitionRenderer.java | 4 ++-- .../hl7/fhir/r5/renderers/LibraryRenderer.java | 6 +++--- .../org/hl7/fhir/r5/renderers/ListRenderer.java | 4 ++-- .../fhir/r5/renderers/NamingSystemRenderer.java | 4 ++-- .../renderers/OperationDefinitionRenderer.java | 2 +- .../r5/renderers/OperationOutcomeRenderer.java | 2 +- .../fhir/r5/renderers/ParametersRenderer.java | 2 +- .../hl7/fhir/r5/renderers/PatientRenderer.java | 6 +++--- .../fhir/r5/renderers/ProvenanceRenderer.java | 4 ++-- .../fhir/r5/renderers/QuestionnaireRenderer.java | 2 +- .../fhir/r5/renderers/RequirementsRenderer.java | 2 +- .../hl7/fhir/r5/renderers/ResourceRenderer.java | 6 +++--- .../r5/renderers/SearchParameterRenderer.java | 4 ++-- .../r5/renderers/SubscriptionTopicRenderer.java | 2 +- .../hl7/fhir/r5/renderers/TestPlanRenderer.java | 10 +++++----- .../r5/renderers/ViewDefinitionRenderer.java | 4 ++-- .../xhtml/HierarchicalTableGenerator.java | 2 +- .../hl7/fhir/utilities/xhtml/XhtmlFluent.java | 9 ++++++++- 27 files changed, 66 insertions(+), 59 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java index 2a323a142b..015642549c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java @@ -252,7 +252,7 @@ public Cell missingCell(HierarchicalTableGenerator gen, String color) { public XhtmlNode renderErrors(ResourceComparison csc) { XhtmlNode div = new XhtmlNode(NodeType.Element, "div"); - XhtmlNode tbl = div.table("grid"); + XhtmlNode tbl = div.table("grid", false); for (ValidationMessage vm : csc.messages) { XhtmlNode tr = tbl.tr(); tr.style("background-color: "+colorForLevel(vm.getLevel())); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSBuilder.java index 67ff07ac68..d8515d82f4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSBuilder.java @@ -102,7 +102,7 @@ private static void addMedications(Bundle bnd, Composition cmp, FHIRToolingClien sct.getCode().addCoding().setSystem("http://loinc.org").setCode("10160-0"); sct.getText().setStatus(NarrativeStatus.GENERATED); var x = sct.getText().getDiv(); - var tbl = x.table("grid"); + var tbl = x.table("grid", false); var tr = tbl.tr(); tr.th().tx("Medication"); tr.th().tx("Category"); @@ -229,7 +229,7 @@ private static void addConditions(Bundle bnd, Composition cmp, FHIRToolingClient sct.getCode().addCoding().setSystem("http://loinc.org").setCode("11450-4"); sct.getText().setStatus(NarrativeStatus.GENERATED); var x = sct.getText().getDiv(); - var tbl = x.table("grid"); + var tbl = x.table("grid", false); var tr = tbl.tr(); tr.th().tx("Code"); tr.th().tx("Category"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSRenderer.java index 823ae39992..ffbbbfeed2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/ips/IPSRenderer.java @@ -63,7 +63,7 @@ public String render(Bundle document) throws IOException { private void generate(XhtmlNode x, Bundle document) { Composition cmp = (Composition) document.getEntryFirstRep().getResource(); int sectionDepth = findSectionDepth(cmp.getSection()); - XhtmlNode table = x.table("grid"); + XhtmlNode table = x.table("grid", false); // row 1: header XhtmlNode tr = table.tr(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ActorDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ActorDefinitionRenderer.java index 4745b81aea..3120e75661 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ActorDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ActorDefinitionRenderer.java @@ -35,7 +35,7 @@ public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingExceptio } public void render(RenderingStatus status, XhtmlNode x, ResourceWrapper acd) throws FHIRFormatError, DefinitionException, IOException { - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.ACTOR_DEF_ACT, context.getTranslated(acd.child("name"))) + " "); tr.td().tx(context.getTranslated(acd.child("title"))); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java index 68148c9139..c1de54e435 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java @@ -652,7 +652,7 @@ private void addMessagingPanel(RenderingStatus status, ResourceWrapper res, Xhtm if(msg.hasEndpoint()) { body.h(nextLevel+1,"msg_end_"+Integer.toString(index)).addText(context.formatPhrase(RenderingContext.CAPABILITY_ENDPOINTS)); - table = body.table("table table-condensed table-hover"); + table = body.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText("Protocol"); tr.th().addText("Address"); @@ -670,7 +670,7 @@ private void addMessagingPanel(RenderingStatus status, ResourceWrapper res, Xhtm if(msg.hasSupportedMessage()) { body.h(nextLevel+1,"msg_end_"+Integer.toString(index)).addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_MSGS)); - table = body.table("table table-condensed table-hover"); + table = body.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText("Mode"); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_DEFINITION)); @@ -697,7 +697,7 @@ private void addDocumentTable(RenderingStatus status, ResourceWrapper res, Xhtml XhtmlNode tbody; XhtmlNode tr; - table = x.table("table table-condensed table-hover"); + table = x.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText("Mode"); tr.th().addText(context.formatPhrase(RenderingContext.CAPABILITY_PROF_RES_DOC)); @@ -879,7 +879,7 @@ private void addSummaryIntro(XhtmlNode x) { } private void addSummaryTable(RenderingStatus status, ResourceWrapper res, XhtmlNode x, CapabilityStatement.CapabilityStatementRestComponent rest, boolean hasVRead, boolean hasPatch, boolean hasDelete, boolean hasHistory, boolean hasUpdates, int count) throws IOException { - XhtmlNode t = x.div().attribute("class","table-responsive").table("table table-condensed table-hover"); + XhtmlNode t = x.div().attribute("class","table-responsive").table("table table-condensed table-hover", false); XhtmlNode tr = t.addTag("thead").tr(); tr.th().b().tx(context.formatPhrase(RenderingContext.CAPABILITY_RES_TYP)); tr.th().b().tx(context.formatPhrase(RenderingContext.GENERAL_PROF)); @@ -986,7 +986,7 @@ private void renderSupportedProfiles(RenderingStatus status, ResourceWrapper res if (r.hasExtension(ToolingExtensions.EXT_PROFILE_MAPPING)) { profCell.br(); profCell.b().tx(context.formatPhrase(RenderingContext.CAPABILITY_PROF_MAP)); - XhtmlNode tbl = profCell.table("grid"); + XhtmlNode tbl = profCell.table("grid", false); boolean doco = false; for (Extension ext : r.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_MAPPING)) { doco = doco || ext.hasExtension("documentation"); @@ -1239,7 +1239,7 @@ private void addExtendedOperations(XhtmlNode body, ResourceOperations ops) { row = body.div().attribute("class", "row"); cell = row.div().attribute("class", "col-12"); addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_EXT_OP)); - table = cell.table("table table-condensed table-hover"); + table = cell.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE)); tr.th().addText(context.formatPhrase(RenderingContext.CAPABILITY_OPER)); @@ -1369,7 +1369,7 @@ private void addSearchParams(XhtmlNode body, ResourceSearchParams sParams) { row = body.div().attribute("class", "row"); cell = row.div().attribute("class", "col-lg-7"); addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_SEARCH_PARS)); - table = cell.table("table table-condensed table-hover"); + table = cell.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE)); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_PAR)); @@ -1385,7 +1385,7 @@ private void addSearchParams(XhtmlNode body, ResourceSearchParams sParams) { cell = row.div().attribute("class", "col-lg-5"); if (!isCombinedEmpty(comboMap)) { addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_COMB_SEARCH_PAR)); - table = cell.table("table table-condensed table-hover"); + table = cell.table("table table-condensed table-hover", false); tr = table.addTag("thead").tr(); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE)); tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_PARS)); 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 42e852683a..7decca4894 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 @@ -114,7 +114,7 @@ public String display(CodeSystem cs) { private void generateFilters(XhtmlNode x, CodeSystem cs) { if (cs.hasFilter()) { x.para().b().tx(formatPhrase(RenderingContext.CODESYSTEM_FILTERS)); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_CODE)); tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_DESC)); @@ -145,7 +145,7 @@ private boolean generateProperties(XhtmlNode x, CodeSystem cs) { x.para().b().tx(formatPhrase(RenderingContext.GENERAL_PROPS)); x.para().b().tx(formatPhrase(RenderingContext.CODESYSTEM_PROPS_DESC)); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); if (hasRendered) { tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_NAME)); @@ -224,7 +224,7 @@ private void generateCodeSystemContent(RenderingStatus status, XhtmlNode x, Code return; } - XhtmlNode t = x.table( "codes"); + XhtmlNode t = x.table( "codes", false); boolean definitions = false; boolean commentS = false; boolean deprecated = false; @@ -272,7 +272,7 @@ private void generateCodeSystemContent(RenderingStatus status, XhtmlNode x, Code if (langs.size() >= 2) { Collections.sort(langs); x.para().b().tx(context.formatPhrase(RenderingContext.GENERAL_ADD_LANG)); - t = x.table("codes"); + t = x.table("codes", false); XhtmlNode tr = t.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); for (String lang : langs) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java index 7faafba35e..078225dd77 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java @@ -406,7 +406,7 @@ public void render(RenderingStatus status, ResourceWrapper res, XhtmlNode x, Con String display; if (ok) { // simple - XhtmlNode tbl = x.table( "grid"); + XhtmlNode tbl = x.table( "grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.CONC_MAP_SOURCE)); tr.td().b().tx(context.formatPhrase(RenderingContext.CONC_MAP_REL)); @@ -456,7 +456,7 @@ public void render(RenderingStatus status, ResourceWrapper res, XhtmlNode x, Con } } - XhtmlNode tbl = x.table( "grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); XhtmlNode td; tr.td().colspan(Integer.toString(1+sources.size())).b().tx(context.formatPhrase(RenderingContext.CONC_MAP_SRC_DET)); @@ -743,7 +743,7 @@ public static XhtmlNode renderMultipleMaps(String start, List maps, Collections.sort(rowSets, new MultipleMappingRowSorter(advisor.sortPolicy(rmmContext) == RenderMultiRowSortPolicy.FIRST_COL)); } XhtmlNode div = new XhtmlNode(NodeType.Element, "div"); - XhtmlNode tbl = div.table("none").style("text-align: left; border-spacing: 0; padding: 5px"); + XhtmlNode tbl = div.table("none", false).style("text-align: left; border-spacing: 0; padding: 5px"); XhtmlNode tr = tbl.tr(); styleCell(tr.td(), false, true, 5).b().tx(start); for (ConceptMap map : maps) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index c5a26bda6d..69a4fc42c5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -48,7 +48,7 @@ public void renderDiagnosticReport(RenderingStatus status, XhtmlNode x, Resource } h2.tx(") "); } - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr; if (dr.has("subject")) { tr = tbl.tr(); @@ -178,7 +178,7 @@ private List fetchObservations(List list) thro } private void buildObservationsTable(RenderingStatus status, XhtmlNode root, List observations, ResourceWrapper eff, ResourceWrapper iss) throws UnsupportedEncodingException, FHIRException, IOException { - XhtmlNode tbl = root.table("grid"); + XhtmlNode tbl = root.table("grid", false); boolean refRange = scanObsForRefRange(observations); boolean flags = scanObsForFlags(observations); boolean note = scanObsForNote(observations); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java index e09d322fc0..55d1f05c7e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java @@ -250,7 +250,7 @@ private String creolLink(String text, String url, String flyover) { } public boolean renderActors(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ExampleScenario scen) throws IOException { - XhtmlNode tbl = x.table("table-striped table-bordered"); + XhtmlNode tbl = x.table("table-striped table-bordered", false); XhtmlNode thead = tbl.tr(); thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); @@ -267,7 +267,7 @@ public boolean renderActors(RenderingStatus status, ResourceWrapper res, XhtmlNo } public boolean renderInstances(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ExampleScenario scen) throws IOException { - XhtmlNode tbl = x.table("table-striped table-bordered"); + XhtmlNode tbl = x.table("table-striped table-bordered", false); XhtmlNode thead = tbl.tr(); thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); @@ -398,7 +398,7 @@ public void renderProcess(RenderingStatus status, XhtmlNode x, ExampleScenarioPr div.para().b().i().tx(context.formatPhrase(RenderingContext.EX_SCEN_POSTCON)); addMarkdown(div, process.getPostConditions()); } - XhtmlNode tbl = div.table("table-striped table-bordered").style("width:100%"); + XhtmlNode tbl = div.table("table-striped table-bordered", false).style("width:100%"); XhtmlNode thead = tbl.tr(); thead.th().addText(context.formatPhrase(RenderingContext.EX_SCEN_STEP)); thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/FeatureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/FeatureDefinitionRenderer.java index 1e02e50840..807294ae76 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/FeatureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/FeatureDefinitionRenderer.java @@ -54,7 +54,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper renderResourceTechDetails(fd, x); genSummaryTable(status, x, fd); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode td = tbl.tr().td(); td.tx("Feature "); @@ -93,7 +93,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper if (fd.has("qualifier")) { td.b().tx("Qualifiers"); - XhtmlNode tbl2 = td.table("lines"); + XhtmlNode tbl2 = td.table("lines", false); XhtmlNode tr = tbl2.tr(); tr.td().b().tx("Name"); tr.td().b().tx("Type"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java index 1e0b027013..b235ee77c2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java @@ -42,7 +42,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper boolean phone = hasCT(authors, "phone") || hasCT(editors, "phone") || hasCT(reviewers, "phone") || hasCT(endorsers, "phone"); boolean url = hasCT(authors, "url") || hasCT(editors, "url") || hasCT(reviewers, "url") || hasCT(endorsers, "url"); x.h2().tx(context.formatPhrase(RenderingContext.LIB_REND_PAR)); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); for (ResourceWrapper cd : authors) { participantRow(status, t, (context.formatPhrase(RenderingContext.LIB_REND_AUT)), cd, email, phone, url); } @@ -60,7 +60,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper List artifacts = lib.children("relatedArtifact"); if (!artifacts.isEmpty()) { x.h2().tx(context.formatPhrase(RenderingContext.LIB_REND_ART)); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); boolean label = false; boolean display = false; boolean citation = false; @@ -76,7 +76,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper List parameters = lib.children("parameter"); if (!parameters.isEmpty()) { x.h2().tx(context.formatPhrase(RenderingContext.GENERAL_PARS)); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); boolean doco = false; for (ResourceWrapper p : parameters) { doco = doco || p.has("documentation"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java index c957b99225..34a790523a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java @@ -33,7 +33,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper if (list.has("title")) { x.h2().tx(list.primitiveValue("title")); } - XhtmlNode t = x.table("clstu"); + XhtmlNode t = x.table("clstu", false); XhtmlNode tr = t.tr(); if (list.has("date")) { tr.td().tx(context.formatPhrase(RenderingContext.LIST_REND_DATE, displayDateTime(list.child("date")))+" "); @@ -75,7 +75,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper deleted = deleted || e.has("deleted"); date = date || e.has("date"); } - t = x.table("grid"); + t = x.table("grid", false); tr = t.tr().style("backgound-color: #eeeeee"); tr.td().b().tx(context.formatPhrase(RenderingContext.LIST_REND_ITEM)); if (date) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/NamingSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/NamingSystemRenderer.java index 444a60ac98..318e2e81f4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/NamingSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/NamingSystemRenderer.java @@ -45,7 +45,7 @@ public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingExceptio public void render(RenderingStatus status, XhtmlNode x, NamingSystem ns) throws FHIRFormatError, DefinitionException, IOException { x.h3().tx(context.formatPhrase(RenderingContext.GENERAL_SUMM)); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); row(tbl, (context.formatPhrase(RenderingContext.GENERAL_DEFINING_URL)), ns.getUrl()); if (ns.hasVersion()) { row(tbl, (context.formatPhrase(RenderingContext.GENERAL_VER)), ns.getVersion()); @@ -90,7 +90,7 @@ public void renderList(XhtmlNode x, List nsl) { } } x.h3().tx(context.formatPhrase(RenderingContext.NAME_SYS_IDEN)); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx((context.formatPhrase(RenderingContext.GENERAL_TYPE))); tr.td().b().tx((context.formatPhrase(RenderingContext.GENERAL_VALUE))); 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 6d7a1563cd..b78db380f8 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 @@ -90,7 +90,7 @@ public void render(RenderingStatus status, XhtmlNode x, OperationDefinition opd) x.h3().tx(context.formatPhrase(RenderingContext.GENERAL_PARS)); //x.para().tx(context.formatPhrase(RenderingContext.GENERAL_PARS)); - XhtmlNode tbl = x.table( "grid"); + XhtmlNode tbl = x.table( "grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.OP_DEF_USE)); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_NAME)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java index 8e9e8ef6ba..b93665bf3b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java @@ -66,7 +66,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper x.para().tx(context.formatPhrase(RenderingContext.OP_OUT_OK)); } if (op.has("issue")) { - XhtmlNode tbl = x.table("grid"); // on the basis that we'll most likely be rendered using the standard fhir css, but it doesn't really matter + XhtmlNode tbl = x.table("grid", false); // on the basis that we'll most likely be rendered using the standard fhir css, but it doesn't really matter XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.OP_OUT_SEV)); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_LOCATION)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java index 2d77121416..c8d1404a16 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java @@ -43,7 +43,7 @@ public ParametersRenderer setMultiLangMode(boolean multiLangMode) { public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { renderResourceTechDetails(r, x); x.h2().tx(context.formatPhrase(RenderingContext.GENERAL_PARS)); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); params(status, tbl, r.children("parameter"), 0); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java index 6d6e5b565a..b18aff4141 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java @@ -144,12 +144,12 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper x.hr(); XhtmlNode tbl; if (hasRenderablePhoto(pat)) { - tbl = x.table("none"); + tbl = x.table("none", true); XhtmlNode tr = tbl.tr(); - tbl = tr.td().table("grid"); + tbl = tr.td().table("grid", false); renderPhoto(tr.td(), pat); } else { - tbl = x.table("grid"); + tbl = x.table("grid", false); } // the table has 4 columns diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProvenanceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProvenanceRenderer.java index 57b0074046..c75d6eb882 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProvenanceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProvenanceRenderer.java @@ -43,7 +43,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper } // summary table x.para().tx(context.formatPhrase(RenderingContext.GENERAL_SUMM)); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr; if (prv.has("occurred")) { tr = t.tr(); @@ -88,7 +88,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper hasOnBehalfOf = hasOnBehalfOf || a.has("onBehalfOf"); } x.para().b().tx(context.formatPhrase(RenderingContext.PROV_AGE)); - t = x.table("grid"); + t = x.table("grid", false); tr = t.tr(); if (hasType) { tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 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 158f91ca77..64237da186 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 @@ -940,7 +940,7 @@ private void renderLinks(RenderingStatus status, XhtmlNode x, ResourceWrapper q) } private void renderDefns(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws IOException { - XhtmlNode tbl = x.table("dict"); + XhtmlNode tbl = x.table("dict", false); renderRootDefinition(status, tbl, q, new ArrayList<>()); for (ResourceWrapper qi : q.children("item")) { renderDefinition(status, tbl, q, qi, new ArrayList<>()); 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 0dfed74f5b..e7c8ddb50a 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 @@ -80,7 +80,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper p.ah(context.prefixLocalHref(c.primitiveValue())).tx(url); } } - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); for (ResourceWrapper stmt : req.children("statement")) { XhtmlNode tr = tbl.tr(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index baeb6c4c7b..b4838142b0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -1076,7 +1076,7 @@ public void renderTable(RenderingStatus status, TableData provider, XhtmlNode x) } } if (columns.size() > 0) { - XhtmlNode table = x.table("grid"); + XhtmlNode table = x.table("grid", false); if (provider.getTitle() != null) { table.tr().td().colspan(columns.size()).b().tx(provider.getTitle()); @@ -1115,7 +1115,7 @@ public void renderOrError(ResourceWrapper dr) throws IOException { public void genSummaryTable(RenderingStatus status, XhtmlNode x, ResourceWrapper cr) throws IOException { if (context.isShowSummaryTable() && cr != null) { - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); genSummaryTableContent(status, tbl, cr); } } @@ -1207,7 +1207,7 @@ protected void genSummaryTableContent(RenderingStatus status, XhtmlNode tbl, Res public void genSummaryTable(RenderingStatus status, XhtmlNode x, CanonicalResource cr) throws IOException { if (context.isShowSummaryTable() && cr != null) { - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); genSummaryTableContent(status, tbl, cr); } } 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 698f627600..95b069233e 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 @@ -66,7 +66,7 @@ public void render(RenderingStatus status, XhtmlNode x, SearchParameter spd) thr p.code().tx(spd.getType().toCode()); addMarkdown(x, spd.getDescription()); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); tr.td().tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_RESOURCE), spd.getBase().size())); XhtmlNode td = tr.td(); @@ -162,7 +162,7 @@ public void render(RenderingStatus status, XhtmlNode x, SearchParameter spd) thr if (spd.hasComponent()) { x.para().b().tx(context.formatPhrase(RenderingContext.GENERAL_COMPARATORS)); - tbl = x.table("grid"); + tbl = x.table("grid", false); for (SearchParameterComponentComponent t : spd.getComponent()) { tr = tbl.tr(); SearchParameter tsp = context.getWorker().fetchResource(SearchParameter.class, t.getDefinition(), spd); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SubscriptionTopicRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SubscriptionTopicRenderer.java index 73d9265ad2..fd386f2a97 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SubscriptionTopicRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SubscriptionTopicRenderer.java @@ -29,7 +29,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper renderResourceTechDetails(st, x); genSummaryTable(status, x, (CanonicalResource) st.getResourceNative()); - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode ttr = tbl.tr(); ttr.td().b().tx("SubscriptionTopic"); ttr.td().tx(context.getTranslated(st.has("title") ? st.child("title") : st.child("name"))); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java index 4a348a7986..ea94986270 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java @@ -89,7 +89,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper ResourceWrapper dep = deps.get(0); p = x.para(); p.b().tx(context.formatPhrase(RenderingContext.TEST_PLAN_DEP)+" "); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr = t.tr(); if (!Utilities.noString(dep.primitiveValue("description"))) { addMarkdown(tr.td(), dep.primitiveValue("description")); @@ -144,7 +144,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper if (deps.size() == 1) { ResourceWrapper dep = deps.get(0); x.h3().addText(context.formatPhrase(RenderingContext.TEST_PLAN_DEP)); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr = t.tr(); if (!Utilities.noString(dep.primitiveValue("description"))) { addMarkdown(tr.td(), dep.primitiveValue("description")); @@ -230,7 +230,7 @@ private void renderTestRun(RenderingStatus status, XhtmlNode x, ResourceWrapper if (trun.has("script")) { ResourceWrapper script = trun.child("script"); - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr = t.tr(); tr.td().b().addText(context.formatPhrase(RenderingContext.TEST_PLAN_LANG)); tr.td().b().addText(context.formatPhrase(RenderingContext.TEST_PLAN_SOURCE)); @@ -249,7 +249,7 @@ private void renderTestRun(RenderingStatus status, XhtmlNode x, ResourceWrapper } private void renderTestData(RenderingStatus status, XhtmlNode x, ResourceWrapper tp, ResourceWrapper tdata) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr = t.tr(); tr.td().b().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); tr.td().b().addText(context.formatPhrase(RenderingContext.GENERAL_CONTENT)); @@ -275,7 +275,7 @@ private void renderTestData(RenderingStatus status, XhtmlNode x, ResourceWrapper } private void renderAssertion(RenderingStatus status, XhtmlNode x, ResourceWrapper tp, ResourceWrapper as) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { - XhtmlNode t = x.table("grid"); + XhtmlNode t = x.table("grid", false); XhtmlNode tr = t.tr(); tr.td().b().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); tr.td().b().addText(context.formatPhrase(RenderingContext.GENERAL_CONTENT)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ViewDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ViewDefinitionRenderer.java index 2d00a815ec..64daee50a9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ViewDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ViewDefinitionRenderer.java @@ -109,7 +109,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper hasNotes = hasNotes || !Utilities.noString(col.getNotes()); } - XhtmlNode t2 = x.table("grid"); + XhtmlNode t2 = x.table("grid", false); XhtmlNode tr = t2.tr(); tr.th().tx("Name"); tr.th().tx("Fhir Type"); @@ -133,7 +133,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper if (vd.has("constant")) { x.para().tx("Constants:"); - XhtmlNode t2 = x.table("grid"); + XhtmlNode t2 = x.table("grid", false); XhtmlNode tr = t2.tr(); tr.th().tx("Name"); tr.th().tx("Value"); 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 944de16a96..0fd71a041e 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 @@ -932,7 +932,7 @@ private XhtmlNode renderCell(XhtmlNode tr, Cell c, String name, String icon, Str XhtmlNode itc = tc; XhtmlNode itr = null; if (c.innerTable) { - itr = tc.table("none").tr(); + itr = tc.table("none", true).tr(); itc = itr.td(); } 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 index 30a5b036e1..e61cbff99b 100644 --- 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 @@ -51,11 +51,18 @@ public XhtmlNode h3() { public XhtmlNode h4() { return addTag("h4"); } - + public XhtmlNode table(String clss) { + return table(clss, false); + } + + public XhtmlNode table(String clss, boolean forPresentation) { XhtmlNode res = addTag("table"); if (!Utilities.noString(clss)) res.setAttribute("class", clss); + if (forPresentation) { + res.setAttribute("role", "presentation"); + } return res; } From c36038cd3f4b816601c4a6d4dbaf8615036f105b Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Jan 2025 14:08:35 +1100 Subject: [PATCH 02/13] Update SNOMED editions related routines (add more editions) --- .../hl7/fhir/r5/renderers/DataRenderer.java | 26 +++++++-- .../fhir/r5/renderers/ValueSetRenderer.java | 55 ++++++++++++------- .../utilities/SnomedUtilities.java | 7 ++- 3 files changed, 60 insertions(+), 28 deletions(-) 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 e86f381a93..f06ffb851e 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 @@ -220,13 +220,29 @@ public static String describeVersion(String version) { case "900000000000207008": return "Intl"+dt; case "731000124108": return "US"+dt; case "32506021000036107": return "AU"+dt; - case "449081005": return "ES"+dt; + case "449081005": return "ES/Intl"+dt; case "554471000005108": return "DK"+dt; case "11000146104": return "NL"+dt; case "45991000052106": return "SE"+dt; - case "999000041000000102": return "UK"+dt; - case "20611000087101": return "CA"+dt; + case "83821000000107": return "UK"+dt; case "11000172109": return "BE"+dt; + case "11000221109" : return "AR"+dt; + case "11000234105" : return "AT"+dt; + case "20621000087109" : return "CA-EN"+dt; + case "20611000087101" : return "CA-FR"+dt; + case "11000181102 " : return "EE"+dt; + case "11000229106" : return "FI"+dt; + case "11000274103" : return "DE"+dt; + case "1121000189102" : return "IN"+dt; + case "11000220105" : return "IE"+dt; + case "21000210109" : return "NZ"+dt; + case "51000202101 " : return "NO"+dt; + case "11000267109" : return "KR"+dt; + case "900000001000122104" : return "ES-ES"+dt; + case "2011000195101" : return "CH"+dt; + case "999000021000000109" : return "UK+Clinical"+dt; + case "5631000179106" : return "UY"+dt; + case "5991000124107" : return "US+ICD10CM"+dt; default: return "??"+dt; } } else { @@ -1946,7 +1962,7 @@ public void renderTriggerDefinition(RenderingStatus status, XhtmlNode x, Resourc renderExpression(status, x, td.child("condition")); } } else { - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); @@ -1976,7 +1992,7 @@ public void renderTriggerDefinition(RenderingStatus status, XhtmlNode x, Resourc } public void renderDataRequirement(RenderingStatus status, XhtmlNode x, ResourceWrapper dr) throws FHIRFormatError, DefinitionException, IOException { - XhtmlNode tbl = x.table("grid"); + XhtmlNode tbl = x.table("grid", false); XhtmlNode tr = tbl.tr(); XhtmlNode td = tr.td().colspan("2"); td.b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index 05d5b912da..e525538f0f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -254,7 +254,7 @@ private void generateExpansion(RenderingStatus status, ResourceWrapper res, Xhtm boolean doInactive = checkDoInactive(vs.getExpansion().getContains()); boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains()); - XhtmlNode t = x.table( "codes"); + XhtmlNode t = x.table("codes", false); XhtmlNode tr = t.tr(); if (doLevel) tr.td().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_LEVEL)); @@ -313,7 +313,7 @@ private void generateExpansion(RenderingStatus status, ResourceWrapper res, Xhtm } else { x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_ADD_DESIG)); } - t = x.table("codes"); + t = x.table("codes", false); tr = t.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); for (String url : designations.keySet()) { @@ -630,23 +630,36 @@ private String formatSCTDate(String ds) { } private String describeModule(String module) { - if ("900000000000207008".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_INT); - if ("731000124108".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_US); - if ("32506021000036107".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_AUS); - if ("449081005".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_SPAN); - if ("554471000005108".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_DANISH); - if ("11000146104".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_DUTCH); - if ("45991000052106".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_SWEDISH); - if ("999000041000000102".equals(module)) - return context.formatPhrase(RenderingContext.VALUE_SET_UK); - return module; + switch (module) { + case "900000000000207008" : context.formatPhrase(RenderingContext.VALUE_SET_INT); + case "449081005" : context.formatPhrase(RenderingContext.VALUE_SET_SPAN); + case "11000221109" : context.formatPhrase(RenderingContext.VALUE_SET_AR); + case "32506021000036107" : context.formatPhrase(RenderingContext.VALUE_SET_AUS); + case "11000234105" : context.formatPhrase(RenderingContext.VALUE_SET_AT); + case "11000172109" : context.formatPhrase(RenderingContext.VALUE_SET_BE); + case "20621000087109" : context.formatPhrase(RenderingContext.VALUE_SET_CA_EN); + case "20611000087101" : context.formatPhrase(RenderingContext.VALUE_SET_CA_FR); + case "554471000005108" : context.formatPhrase(RenderingContext.VALUE_SET_DANISH); + case "11000181102 " : context.formatPhrase(RenderingContext.VALUE_SET_EE); + case "11000229106" : context.formatPhrase(RenderingContext.VALUE_SET_FI); + case "11000274103" : context.formatPhrase(RenderingContext.VALUE_SET_DE); + case "1121000189102" : context.formatPhrase(RenderingContext.VALUE_SET_IN); + case "11000220105" : context.formatPhrase(RenderingContext.VALUE_SET_IE); + case "11000146104" : context.formatPhrase(RenderingContext.VALUE_SET_DUTCH); + case "21000210109" : context.formatPhrase(RenderingContext.VALUE_SET_NZ); + case "51000202101 " : context.formatPhrase(RenderingContext.VALUE_SET_NO); + case "11000267109" : context.formatPhrase(RenderingContext.VALUE_SET_KR); + case "900000001000122104" : context.formatPhrase(RenderingContext.VALUE_ES_ES); + case "45991000052106" : context.formatPhrase(RenderingContext.VALUE_SET_SWEDISH); + case "2011000195101" : context.formatPhrase(RenderingContext.VALUE_SET_CH); + case "83821000000107" : context.formatPhrase(RenderingContext.VALUE_SET_UK); + case "999000021000000109" : context.formatPhrase(RenderingContext.VALUE_SET_UK_CLIN); + case "5631000179106" : context.formatPhrase(RenderingContext.VALUE_SET_UY); + case "731000124108" : context.formatPhrase(RenderingContext.VALUE_SET_US); + case "5991000124107" : context.formatPhrase(RenderingContext.VALUE_SET_US_ICD10CM); + default: + return module; + } } private boolean hasVersionParameter(ValueSetExpansionComponent expansion) { @@ -1001,7 +1014,7 @@ private void generateComposition(RenderingStatus status, ResourceWrapper res, Xh } else { x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_ADD_DESIG)); } - XhtmlNode t = x.table("codes"); + XhtmlNode t = x.table("codes", false); XhtmlNode tr = t.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); for (String url : designations.keySet()) { @@ -1194,7 +1207,7 @@ private void genInclude(RenderingStatus status, XhtmlNode ul, ConceptSetComponen definitions = getConceptsForCodes(e, inc, vsRes, index); - XhtmlNode t = li.table("none"); + XhtmlNode t = li.table("none", false); boolean hasComments = false; boolean hasDefinition = false; for (ConceptReferenceComponent c : inc.getConcept()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/SnomedUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/SnomedUtilities.java index 07f009626c..f71b556c41 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/SnomedUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/SnomedUtilities.java @@ -45,8 +45,11 @@ public static String getEditionFromVersion(String version) { if (version.contains("/")) { version = version.substring(0, version.indexOf("/")); } - if (Utilities.existsInList(version, "900000000000207008", "731000124108", "32506021000036107", "11000172109", "20611000087101", - "449081005", "554471000005108", "11000146104", "45991000052106", "2011000195101", "83821000000107", "827022005")) { + if (Utilities.existsInList(version, "900000000000207008", "449081005", "11000221109", "32506021000036107", "11000234105", "11000172109", + "20621000087109", "20611000087101", "554471000005108", "11000181102", "11000229106", + "11000274103", "1121000189102", "11000220105", "11000146104", "21000210109", "51000202101", + "11000267109", "900000001000122104", "45991000052106", "2011000195101", "83821000000107", + "999000021000000109", "5631000179106", "731000124108", "599100012410")) { return version; } else { return null; From 4ccaae1571e14f61c8b5955cf2160e47a0736a94 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Jan 2025 14:09:52 +1100 Subject: [PATCH 03/13] Lookup compliesWithProfile target from link-only dependencies --- .../StructureDefinitionRenderer.java | 17 ++++++++----- .../r5/renderers/utils/RenderingContext.java | 25 +++++++++++++++++++ .../utilities/i18n/RenderingI18nContext.java | 17 +++++++++++++ .../resources/rendering-phrases.properties | 22 ++++++++++++++++ .../fhir/validation/cli/model/CliContext.java | 24 ++++++++++++++++-- 5 files changed, 97 insertions(+), 8 deletions(-) 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 f273cfe528..76a69e7f0a 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 @@ -126,7 +126,7 @@ public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper StructureDefinition sd = (StructureDefinition) r.getBase(); genSummaryTable(status, x, sd); if (context.getStructureMode() == StructureDefinitionRendererMode.DATA_DICT) { - renderDict(status, sd, sd.getDifferential().getElement(), x.table("dict"), false, GEN_MODE_DIFF, "", r); + renderDict(status, sd, sd.getDifferential().getElement(), x.table("dict", false), false, GEN_MODE_DIFF, "", r); } else { x.addChildNode(generateTable(status, context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getLink(KnownLinkType.SPEC), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, context.withUniqueLocalPrefix(null), "r", r)); @@ -1851,7 +1851,7 @@ private void getAncestorElements(StructureDefinition profile, List list, String start, boolean bold) { + private void addCanonicalListExt(HierarchicalTableGenerator gen, Cell c, List list, String start, boolean bold) throws IOException { List clist = new ArrayList<>(); for (Extension ext : list) { if (ext.hasValueCanonicalType()) { @@ -1861,7 +1861,7 @@ private void addCanonicalListExt(HierarchicalTableGenerator gen, Cell c, List list, String start, boolean bold) { + private void addCanonicalList(HierarchicalTableGenerator gen, Cell c, List list, String start, boolean bold) throws IOException { if (!list.isEmpty()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } @@ -1880,6 +1880,9 @@ private void addCanonicalList(HierarchicalTableGenerator gen, Cell c, List T findLinkableResource(Class class_, String uri) throws IOException; + } + public static class RenderingContextLangs { private RenderingContext defLangRC; @@ -297,6 +303,7 @@ public enum MultiLanguagePolicy { private String uniqueLocalPrefix; private Set anchors = new HashSet<>(); private boolean unknownLocalReferencesNotLinks; + private IResourceLinkResolver resolveLinkResolver; /** * @@ -366,6 +373,7 @@ public RenderingContext copy(boolean copyAnchors) { res.anchors = anchors; } res.unknownLocalReferencesNotLinks = unknownLocalReferencesNotLinks; + res.resolveLinkResolver = resolveLinkResolver; return res; } @@ -1058,4 +1066,21 @@ public boolean isUnknownLocalReferencesNotLinks() { public void setUnknownLocalReferencesNotLinks(boolean unknownLocalReferencesNotLinks) { this.unknownLocalReferencesNotLinks = unknownLocalReferencesNotLinks; } + + public T findLinkableResource(Class class_, String uri) throws IOException { + if (resolveLinkResolver == null) { + return null; + } else { + return resolveLinkResolver.findLinkableResource(class_, uri); + } + } + + public IResourceLinkResolver getResolveLinkResolver() { + return resolveLinkResolver; + } + + public void setResolveLinkResolver(IResourceLinkResolver resolveLinkResolver) { + this.resolveLinkResolver = resolveLinkResolver; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java index 4b054001dd..8d6c27d382 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java @@ -906,6 +906,23 @@ public class RenderingI18nContext extends I18nBase { public static final String VALUE_SET_USED_ELSEWHERE = "VALUE_SET_USED_ELSEWHERE"; public static final String VALUE_SET_WHERE = "VALUE_SET_WHERE"; public static final String VALUE_SET_WHERE_CODES = "VALUE_SET_WHERE_CODES"; + public static final String VALUE_SET_AR = "VALUE_SET_AR"; + public static final String VALUE_SET_AT = "VALUE_SET_AT"; + public static final String VALUE_SET_BE = "VALUE_SET_BE"; + public static final String VALUE_SET_CA_EN = "VALUE_SET_CA_EN"; + public static final String VALUE_SET_CA_FR = "VALUE_SET_CA_FR"; + public static final String VALUE_SET_EE = "VALUE_SET_EE"; + public static final String VALUE_SET_FI = "VALUE_SET_FI"; + public static final String VALUE_SET_DE = "VALUE_SET_DE"; + public static final String VALUE_SET_IE = "VALUE_SET_IE"; + public static final String VALUE_SET_NZ = "VALUE_SET_NZ"; + public static final String VALUE_SET_NO = "VALUE_SET_NO"; + public static final String VALUE_SET_KR = "VALUE_SET_KR"; + public static final String VALUE_ES_ES = "VALUE_ES_ES"; + public static final String VALUE_SET_CH = "VALUE_SET_CH"; + public static final String VALUE_SET_UK_CLIN = "VALUE_SET_UK_CLIN"; + public static final String VALUE_SET_UY = "VALUE_SET_UY"; + public static final String VALUE_SET_US_ICD10CM = "VALUE_SET_US_ICD10CM"; public static final String VS_ABSTRACT_CODE_HINT = "VS_ABSTRACT_CODE_HINT"; public static final String GENERAL_CODE = "GENERAL_CODE"; public static final String GENERAL_DESC = "GENERAL_DESC"; diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties index cfb7839b17..decd77d61c 100644 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties +++ b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties @@ -947,3 +947,25 @@ DATA_REND_EVENING_LATE = Late Evening DATA_REND_NIGHT = Night DATA_REND_AFTER_SLEEP = After Sleep DATA_REND_IMMEDIATE = Immediate +VALUE_SET_AR = Argentinian Edition +VALUE_SET_AT = Austrian Edition +VALUE_SET_BE = Belgian Edition +VALUE_SET_CA_EN = Canadian English Edition +VALUE_SET_CA_FR = Canadian Canadian French Edition +VALUE_SET_EE = Estonian Edition +VALUE_SET_FI = Finnish Edition +VALUE_SET_DE = German Edition +VALUE_SET_IE = Irish Edition +VALUE_SET_NZ = New Zealand Edition +VALUE_SET_NO = Norwegian Edition +VALUE_SET_KR = Republic of Korea Edition (South Korea) +VALUE_ES_ES = Spanish National Edition +VALUE_SET_CH = Swiss Edition +VALUE_SET_UK_CLIN = UK Clinical Edition +VALUE_SET_UY = Uruguay Edition +VALUE_SET_US_ICD10CM = US Edition (with ICD-10-CM maps) + + + + + \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index f583ea17e7..57100e9578 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -878,13 +878,33 @@ public CliContext setFhirpath(String fhirpath) { public String getSnomedCTCode() { if ("intl".equals(snomedCT)) return "900000000000207008"; if ("us".equals(snomedCT)) return "731000124108"; - if ("uk".equals(snomedCT)) return "999000041000000102"; + if ("us+icd10cm".equals(snomedCT)) return "5991000124107"; + if ("uk/clinical".equals(snomedCT)) return "999000021000000109"; + if ("uk".equals(snomedCT)) return "83821000000107"; if ("au".equals(snomedCT)) return "32506021000036107"; - if ("ca".equals(snomedCT)) return "20611000087101"; + if ("at".equals(snomedCT)) return "11000234105"; + if ("ca".equals(snomedCT)) return "20621000087109"; + if ("ca-en".equals(snomedCT)) return "20621000087109"; + if ("ca-fr".equals(snomedCT)) return "20611000087101"; if ("nl".equals(snomedCT)) return "11000146104"; if ("se".equals(snomedCT)) return "45991000052106"; if ("es".equals(snomedCT)) return "449081005"; + if ("es-ES".equals(snomedCT)) return "900000001000122104"; + if ("ar".equals(snomedCT)) return "11000221109"; if ("dk".equals(snomedCT)) return "554471000005108"; + if ("be".equals(snomedCT)) return "11000172109"; + if ("ee".equals(snomedCT)) return "11000181102"; + if ("fi".equals(snomedCT)) return "11000229106"; + if ("de".equals(snomedCT)) return "11000274103"; + if ("in".equals(snomedCT)) return "1121000189102"; + if ("ie".equals(snomedCT)) return "11000220105"; + if ("nl".equals(snomedCT)) return "11000146104"; + if ("nz".equals(snomedCT)) return "21000210109"; + if ("no".equals(snomedCT)) return "51000202101"; + if ("kr".equals(snomedCT)) return "11000267109"; + if ("se".equals(snomedCT)) return "45991000052106"; + if ("ch".equals(snomedCT)) return "2011000195101"; + if ("uy".equals(snomedCT)) return "5631000179106"; return snomedCT; } From 63e2ca36da674d759755d92915560dcd84a88127 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Jan 2025 14:10:27 +1100 Subject: [PATCH 04/13] Validate earliestAllowed and latestAllowed FHIRVersion for extensions --- .../org/hl7/fhir/r5/utils/ToolingExtensions.java | 2 ++ .../org/hl7/fhir/utilities/VersionUtilities.java | 4 +++- .../hl7/fhir/utilities/i18n/I18nConstants.java | 2 ++ .../src/main/resources/Messages.properties | 5 +++-- .../validation/instance/InstanceValidator.java | 14 ++++++++++++++ .../fhir/validation/tests/ValidationTests.java | 16 ++++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) 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 377e1a4bd7..4411048752 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 @@ -281,6 +281,8 @@ public class ToolingExtensions { public static final String EXT_SUPPRESS_RESOURCE_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/json-suppress-resourcetype"; public static final String EXT_PROFILE_VIEW_HINT = "http://hl7.org/fhir/tools/StructureDefinition/view-hint"; public static final String EXT_SNAPSHOT_BEHAVIOR = "http://hl7.org/fhir/tools/StructureDefinition/snapshot-behavior"; + public static final String EXT_EARLIEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/earliestAllowedFHIRVersion"; + public static final String EXT_LATEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/latestAllowedFHIRVersion"; // specific extension helpers diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index c30e0674bf..aa013aa0a6 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -87,6 +87,8 @@ private int compareString(String s1, String s2) { private int compareInteger(String s1, String s2) { if (s1 == null) { return s2 == null ? 0 : 1; + } else if (s2 == null) { + return -1; } else { return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2)); } @@ -329,7 +331,7 @@ public static boolean isSemVer(String version) { if (Utilities.noString(version)) { return false; } - return version.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-\\+]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-\\+][0-9a-zA-Z-\\+]*))*))?$"); + return version.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-\\+]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-\\+][0-9a-zA-Z-\\+]*))*))?)?$"); } /** diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 894a4638c0..7b00f01d4b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -1165,4 +1165,6 @@ public class I18nConstants { public static final String NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR = "NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR"; public static final String CONCEPTMAP_VS_NOT_A_VS = "CONCEPTMAP_VS_NOT_A_VS"; public static final String SD_DERIVATION_NO_CONCRETE = "SD_DERIVATION_NO_CONCRETE"; + public static final String EXTENSION_FHIR_VERSION_EARLIEST = "EXTENSION_FHIR_VERSION_EARLIEST"; + public static final String EXTENSION_FHIR_VERSION_LATEST = "EXTENSION_FHIR_VERSION_LATEST"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index e350e1a4bb..36402fd834 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1197,5 +1197,6 @@ VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'', but no erro VS_EXP_IMPORT_ERROR_TOO_COSTLY = Unable to expand excluded value set ''{0}'': too costly VS_EXP_FILTER_UNK = ValueSet ''{0}'' Filter by property ''{1}'' and op ''{2}'' is not supported yet CONCEPTMAP_VS_NOT_A_VS = Reference must be to a ValueSet, but found a {0} instead -SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendents were found (check definitions - this is usually an error unless concrete definitions are in some other package) - \ No newline at end of file +SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendants were found (check definitions - this is usually an error unless concrete definitions are in some other package) +EXTENSION_FHIR_VERSION_EARLIEST = The definition of the extension ''{0}'' specifies that the earliest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4}) +EXTENSION_FHIR_VERSION_LATEST = The definition of the extension ''{0}'' specifies that the latest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4}) \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index f84957a41a..92df9b87ae 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -2536,6 +2536,20 @@ private boolean checkExtensionContext(Object appContext, List } Collections.sort(plist); // logical paths are a set, but we want a predictable order for error messages + if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION) || definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) { + if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION)) { + String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_EARLIEST_FHIR_VERSION); + ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(), + VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) >= 0, + I18nConstants.EXTENSION_FHIR_VERSION_EARLIEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok; + } + if (definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) { + String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_LATEST_FHIR_VERSION); + ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(), + VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) <= 0, + I18nConstants.EXTENSION_FHIR_VERSION_LATEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok; + } + } for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { if (ok) { break; diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index 1ba16e3e08..2ff1884ab7 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -298,6 +298,18 @@ public void test() throws Exception { } } } + if (content.has("supporting5")) { + for (JsonElement e : content.getAsJsonArray("supporting5")) { + String filename = e.getAsString(); + String contents = TestingUtilities.loadTestResource("validator", filename); + CanonicalResource mr = (CanonicalResource) loadResource(filename, contents, "5.0.0"); + logOutput("load resource "+mr.getUrl()); + val.getContext().cacheResource(mr); + if (mr instanceof ImplementationGuide) { + val.getImplementationGuides().add((ImplementationGuide) mr); + } + } + } val.getBundleValidationRules().clear(); if (content.has("bundle-param")) { val.getBundleValidationRules().add(new BundleValidationRule().setRule(content.getAsJsonObject("bundle-param").get("rule").getAsString()).setProfile( content.getAsJsonObject("bundle-param").get("profile").getAsString())); @@ -520,6 +532,10 @@ public StructureDefinition loadProfile(String filename, String contents, List Date: Tue, 7 Jan 2025 16:39:02 +1100 Subject: [PATCH 05/13] resolve issues with references between IGs to example resources --- .../fhir/r5/renderers/ResourceRenderer.java | 41 +++++++++++-------- .../r5/renderers/utils/RenderingContext.java | 10 +++++ .../hl7/fhir/r5/renderers/utils/Resolver.java | 4 +- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index b4838142b0..6dbac11470 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -177,7 +177,7 @@ public String canonicalTitle(ResourceWrapper r) { if (r.has("id")) { return r.primitiveValue("id"); } - return "??"; + return "?title?"; } public void describe(XhtmlNode x, ResourceWrapper r) throws UnsupportedEncodingException, IOException { @@ -283,7 +283,7 @@ protected String displayReference(ResourceWrapper type) { } else if (id != null) { return "id: "+displayIdentifier(id); } else { - return "??"; + return "?ref?"; } } @@ -361,13 +361,13 @@ public void renderReference(RenderingStatus status, XhtmlNode x, ResourceWrapper x.code().tx(disp); } } else if (rr.getResource() == null) { - String disp = display != null && display.hasPrimitiveValue() ? displayDataType(display) : "??"; + String disp = display != null && display.hasPrimitiveValue() ? displayDataType(display) : rr.getUrlReference(); x.ah(context.prefixLocalHref(rr.getWebPath())).tx(disp); } else if (rr.getResource() != null) { String disp = display != null && display.hasPrimitiveValue() ? displayDataType(display) : RendererFactory.factory(rr.getResource(), context.forContained()).buildSummary(rr.getResource()); x.ah(context.prefixLocalHref(rr.getWebPath())).tx(disp); } else { - String disp = display != null && display.hasPrimitiveValue() ? displayDataType(display) : "??"; + String disp = display != null && display.hasPrimitiveValue() ? displayDataType(display) : "?rref2?"; x.ah(context.prefixLocalHref(rr.getWebPath())).tx(disp); } } else if (display != null && id != null) { @@ -381,7 +381,7 @@ public void renderReference(RenderingStatus status, XhtmlNode x, ResourceWrapper x.tx("Identifier: "); renderIdentifier(status, x, id); } else { - x.tx("??"); + x.tx("?rref?"); } checkRenderExtensions(status, x, type); } @@ -436,7 +436,7 @@ public void renderReference(ResourceWrapper res, HierarchicalTableGenerator gen, } else if (r.hasIdentifier()) { text.append(displayIdentifier(wrapWC(res, r.getIdentifier()))); } else { - text.append("??"); + text.append("?r-ref?"); } } else if (context.isTechnicalMode()) { text.append(r.getReference()); @@ -511,7 +511,7 @@ public void renderReference(ResourceWrapper res, HierarchicalTableGenerator gen, } else if (r.has("identifier")) { text.append(displayIdentifier(r.child("identifier"))); } else { - text.append("??"); + text.append("?r-ref2?"); } } else if (context.isTechnicalMode()) { text.append(r.primitiveValue("reference")); @@ -693,7 +693,7 @@ private ResourceWrapper findContainer(ResourceWrapper resource) { return container; } - private ResourceWithReference resolveOutside(ResourceWrapper resource, String url, String version, boolean followLinks) { + private ResourceWithReference resolveOutside(ResourceWrapper resource, String url, String version, boolean followLinks) throws IOException { ResourceWrapper container = findContainer(resource); if (container != null) { while (container != null) { @@ -746,7 +746,7 @@ private ResourceWithReference findInBundle(ResourceWrapper resource, String url) return null; } - protected ResourceWithReference resolveReference(ResourceWrapper resource, String url, boolean followLinks) { + protected ResourceWithReference resolveReference(ResourceWrapper resource, String url, boolean followLinks) throws IOException { if (url == null) { return null; } @@ -766,16 +766,23 @@ protected ResourceWithReference resolveReference(ResourceWrapper resource, Strin } protected ResourceWithReference resolveReference(ResourceWrapper reference) { - if (reference.fhirType().equals("CodeableReference")) { - if (reference.has("reference")) { - return resolveReference(reference.child("reference")); + try { + if (reference.fhirType().equals("CodeableReference")) { + if (reference.has("reference")) { + return resolveReference(reference.child("reference")); + } else { + return null; + } + } else if (reference.fhirType().equals("Reference")) { + return resolveReference(reference.getResourceWrapper(), reference.primitiveValue("reference"), true); } else { - return null; + return resolveReference(reference.getResourceWrapper(), reference.primitiveValue(), true); } - } else if (reference.fhirType().equals("Reference")) { - return resolveReference(reference.getResourceWrapper(), reference.primitiveValue("reference"), true); - } else { - return resolveReference(reference.getResourceWrapper(), reference.primitiveValue(), true); + } catch (IOException e) { + if (context.isDebug()) { + e.printStackTrace(); + } + return null; } } 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 d6dbddcf34..94dda11ac7 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 @@ -304,6 +304,7 @@ public enum MultiLanguagePolicy { private Set anchors = new HashSet<>(); private boolean unknownLocalReferencesNotLinks; private IResourceLinkResolver resolveLinkResolver; + private boolean debug; /** * @@ -374,6 +375,7 @@ public RenderingContext copy(boolean copyAnchors) { } res.unknownLocalReferencesNotLinks = unknownLocalReferencesNotLinks; res.resolveLinkResolver = resolveLinkResolver; + res.debug = debug; return res; } @@ -1083,4 +1085,12 @@ public void setResolveLinkResolver(IResourceLinkResolver resolveLinkResolver) { this.resolveLinkResolver = resolveLinkResolver; } + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java index 6d3386231b..21904f8af9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java @@ -1,10 +1,12 @@ package org.hl7.fhir.r5.renderers.utils; +import java.io.IOException; + public class Resolver { public interface IReferenceResolver { - ResourceWithReference resolve(RenderingContext context, String url, String version); + ResourceWithReference resolve(RenderingContext context, String url, String version) throws IOException; /** * returns the correct literal URL for the specified logical uri From df72ca604e971a5754dff663a1ca03ab3b5d4826 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 08:10:12 +1100 Subject: [PATCH 06/13] Error when ValueSet.compose.include.system refers to a ValueSet --- .../terminologies/TerminologyUtilities.java | 28 ++++++++++++++++++- .../hl7/fhir/r5/utils/ToolingExtensions.java | 5 ++-- .../fhir/utilities/i18n/I18nConstants.java | 7 +++++ .../src/main/resources/Messages.properties | 9 +++++- .../instance/type/ValueSetValidator.java | 24 +++++++++++++--- .../5.0.0/cs-externals.json | 3 +- 6 files changed, 67 insertions(+), 9 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java index 6dc1a7c9d6..3f6710e47a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyUtilities.java @@ -1,11 +1,14 @@ package org.hl7.fhir.r5.terminologies; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.Identifier; -import org.hl7.fhir.utilities.json.model.JsonObject; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.utilities.Utilities; public class TerminologyUtilities { @@ -24,4 +27,27 @@ public static Set listOids(CanonicalResource cr) { } return oids; } + + public static List listSystems(ValueSet vs) { + Set res = new HashSet<>(); + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + if (inc.hasSystem()) { + if (inc.hasVersion()) { + res.add(inc.getSystem()+"|"+inc.getVersion()); + } else { + res.add(inc.getSystem()); + } + } + } + for (ConceptSetComponent inc : vs.getCompose().getExclude()) { + if (inc.hasSystem()) { + if (inc.hasVersion()) { + res.add(inc.getSystem()+"|"+inc.getVersion()); + } else { + res.add(inc.getSystem()); + } + } + } + return Utilities.sorted(res); + } } 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 4411048752..7f77419d45 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 @@ -281,8 +281,9 @@ public class ToolingExtensions { public static final String EXT_SUPPRESS_RESOURCE_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/json-suppress-resourcetype"; public static final String EXT_PROFILE_VIEW_HINT = "http://hl7.org/fhir/tools/StructureDefinition/view-hint"; public static final String EXT_SNAPSHOT_BEHAVIOR = "http://hl7.org/fhir/tools/StructureDefinition/snapshot-behavior"; - public static final String EXT_EARLIEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/earliestAllowedFHIRVersion"; - public static final String EXT_LATEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/latestAllowedFHIRVersion"; + public static final String EXT_FHIRVERSION_SPECIFIC_USE = "http://hl7.org/fhir/StructureDefinition/version-specific-use"; + public static final String EXT_FHIRVERSION_SPECIFIC_USE_START = "startFhirVersion"; + public static final String EXT_FHIRVERSION_SPECIFIC_USE_END = "endFhirVersion"; // specific extension helpers diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 7b00f01d4b..c6c8f05b4d 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -227,8 +227,12 @@ public class I18nConstants { public static final String EXTENSION_CONTEXT_UNABLE_TO_FIND_PROFILE = "EXTENSION_CONTEXT_UNABLE_TO_FIND_PROFILE"; public static final String EXTENSION_EXTM_CONTEXT_WRONG = "Extension_EXTM_Context_Wrong"; public static final String EXTENSION_EXTM_CONTEXT_WRONG_XVER = "EXTENSION_EXTM_CONTEXT_WRONG_XVER"; + public static final String EXTENSION_EXTM_CONTEXT_WRONG_VER = "Extension_EXTM_Context_Wrong_VER"; + public static final String EXTENSION_EXTM_CONTEXT_WRONG_XVER_VER = "EXTENSION_EXTM_CONTEXT_WRONG_XVER_VER"; public static final String EXTENSION_EXTP_CONTEXT_WRONG = "Extension_EXTP_Context_Wrong"; public static final String EXTENSION_EXTP_CONTEXT_WRONG_XVER = "EXTENSION_EXTP_CONTEXT_WRONG_XVER"; + public static final String EXTENSION_EXTP_CONTEXT_WRONG_VER = "Extension_EXTP_Context_Wrong_VER"; + public static final String EXTENSION_EXTP_CONTEXT_WRONG_XVER_VER = "EXTENSION_EXTP_CONTEXT_WRONG_XVER_VER"; public static final String EXTENSION_EXT_COUNT_MISMATCH = "Extension_EXT_Count_Mismatch"; public static final String EXTENSION_EXT_COUNT_NOTFOUND = "Extension_EXT_Count_NotFound"; public static final String EXTENSION_EXT_FIXED_BANNED = "Extension_EXT_Fixed_Banned"; @@ -1167,4 +1171,7 @@ public class I18nConstants { public static final String SD_DERIVATION_NO_CONCRETE = "SD_DERIVATION_NO_CONCRETE"; public static final String EXTENSION_FHIR_VERSION_EARLIEST = "EXTENSION_FHIR_VERSION_EARLIEST"; public static final String EXTENSION_FHIR_VERSION_LATEST = "EXTENSION_FHIR_VERSION_LATEST"; + public static final String VALUESET_INCLUDE_WRONG_VS = "VALUESET_INCLUDE_WRONG_VS"; + public static final String VALUESET_INCLUDE_WRONG_VS_HINT = "VALUESET_INCLUDE_WRONG_VS_HINT"; + public static final String VALUESET_INCLUDE_WRONG_VS_MANY = "VALUESET_INCLUDE_WRONG_VS_MANY"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 36402fd834..179613c3ae 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -200,7 +200,9 @@ ERROR_GENERATING_SNAPSHOT = Error generating Snapshot: {0} (this usually arises EXTENSION_CONTEXT_UNABLE_TO_CHECK_PROFILE = The extension {0} specifies a context of {1} but the validator cannot check whether the profile is valid or not at this time EXTENSION_CONTEXT_UNABLE_TO_FIND_PROFILE = The extension {0} specifies a context of {1} but the validator cannot find that profile EXTENSION_EXTM_CONTEXT_WRONG_XVER = The modifier extension {0} from FHIR version {3} is not allowed to be used at this point (allowed = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions) +EXTENSION_EXTM_CONTEXT_WRONG_XVER_VER = The modifier extension {0} from FHIR version {3} is not allowed to be used at this point (allowed for this version = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions) EXTENSION_EXTP_CONTEXT_WRONG_XVER = The extension {0} from FHIR version {3} is not allowed to be used at this point (allowed = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions) +EXTENSION_EXTP_CONTEXT_WRONG_XVER_VER = The extension {0} from FHIR version {3} is not allowed to be used at this point (allowed for this version = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions) EXT_VER_URL_IGNORE = Extension URLs don''t have versions. The validator has ignored the version and processed the extension anyway EXT_VER_URL_MISLEADING = The extension URL contains a ''|'' which makes it look like a versioned URL, but it''s not, and this is confusing for implementers EXT_VER_URL_NOT_ALLOWED = The extension URL must not contain a version @@ -228,7 +230,9 @@ Error_parsing_XHTML_ = Error parsing XHTML: {0} Error_reading__from_package__ = Error reading {0} from package {1}#{2}: {3} Error_validating_code_running_without_terminology_services = Unable to validate code ''{0}'' in system ''{1}'' because the validator is running without terminology services Extension_EXTM_Context_Wrong = The modifier extension {0} is not allowed to be used at this point (allowed = {1}; this element is {2}) +Extension_EXTM_Context_Wrong_VER = The modifier extension {0} is not allowed to be used at this point (allowed for this version = {1}; this element is {2}) Extension_EXTP_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is {2}) +Extension_EXTP_Context_Wrong_VER = The extension {0} is not allowed to be used at this point (allowed for this version = {1}; this element is {2}) Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn''t contain any extensions @@ -1199,4 +1203,7 @@ VS_EXP_FILTER_UNK = ValueSet ''{0}'' Filter by property ''{1}'' and op ''{2}'' i CONCEPTMAP_VS_NOT_A_VS = Reference must be to a ValueSet, but found a {0} instead SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendants were found (check definitions - this is usually an error unless concrete definitions are in some other package) EXTENSION_FHIR_VERSION_EARLIEST = The definition of the extension ''{0}'' specifies that the earliest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4}) -EXTENSION_FHIR_VERSION_LATEST = The definition of the extension ''{0}'' specifies that the latest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4}) \ No newline at end of file +EXTENSION_FHIR_VERSION_LATEST = The definition of the extension ''{0}'' specifies that the latest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4}) +VALUESET_INCLUDE_WRONG_VS = The system ''{0}'' is actually a value set +VALUESET_INCLUDE_WRONG_VS_HINT = The system ''{0}'' is actually a value set, which itself refers to the system ''{1}'' so that may be what is intended here +VALUESET_INCLUDE_WRONG_VS_MANY = The system ''{0}'' is actually a value set, which itself refers to the systems {1} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index 05555971f6..9578726db8 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -18,6 +18,7 @@ import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r5.terminologies.TerminologyUtilities; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest; import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; @@ -303,6 +304,7 @@ private boolean validateValueSetInclude(ValidationContext valContext, List systems = TerminologyUtilities.listSystems(vs); + if (systems.size() == 0) { + ok = rule(errors, "2025-01-09", IssueType.INVALID, stack, false, I18nConstants.VALUESET_INCLUDE_WRONG_VS, system) && ok; + } else if (systems.size() == 1) { + ok = rule(errors, "2025-01-09", IssueType.INVALID, stack, false, I18nConstants.VALUESET_INCLUDE_WRONG_VS_HINT, system, systems.get(0)) && ok; + } else { + ok = rule(errors, "2025-01-09", IssueType.INVALID, stack, false, I18nConstants.VALUESET_INCLUDE_WRONG_VS_MANY, system, CommaSeparatedStringBuilder.join(", ", systems)) && ok; + } + } + } - if (!noTerminologyChecks) { + if (!noTerminologyChecks && validateConcepts) { boolean systemOk = true; int cc = 0; List batch = new ArrayList<>(); @@ -336,7 +352,7 @@ private boolean validateValueSetInclude(ValidationContext valContext, List errors, String vsid, } - private boolean validateValueSetIncludeConcept(List errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, CodeSystemChecker slv) { + private boolean validateValueSetIncludeConcept(List errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, CodeSystemChecker slv, boolean locallyKnownCodeSystem) { String code = concept.getChildValue("code"); String display = concept.getChildValue("display"); slv.checkConcept(code, display); diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/cs-externals.json b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/cs-externals.json index fdb076af77..9fcfb03189 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/cs-externals.json +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/cs-externals.json @@ -2,5 +2,6 @@ "#cs1|null" : null, "#cs|null" : null, "http://something|null" : null, - "http://snomed.info/sct|null" : null + "http://snomed.info/sct|null" : null, + "http://hl7.org/fhir/ValueSet/FHIR-version|5.0.0" : null } From e2a8c6b613cece8b241e001677a4abc034d5f329 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 08:11:53 +1100 Subject: [PATCH 07/13] Validator enforce value-set-specific value for Extension and Extension context --- .../instance/InstanceValidator.java | 168 ++++++++++-------- 1 file changed, 94 insertions(+), 74 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 92df9b87ae..b333d93972 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -2536,110 +2536,130 @@ private boolean checkExtensionContext(Object appContext, List } Collections.sort(plist); // logical paths are a set, but we want a predictable order for error messages - if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION) || definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) { - if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION)) { - String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_EARLIEST_FHIR_VERSION); + if (definition.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE)) { + Extension ext = definition.getExtensionByUrl(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE); + if (ext.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_START)) { + String v = ToolingExtensions.readStringExtension(ext, ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_START); ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(), VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) >= 0, I18nConstants.EXTENSION_FHIR_VERSION_EARLIEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok; } - if (definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) { - String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_LATEST_FHIR_VERSION); + if (ext.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_END)) { + String v = ToolingExtensions.readStringExtension(ext, ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_END); ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(), VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) <= 0, I18nConstants.EXTENSION_FHIR_VERSION_LATEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok; } } + boolean vv = false; for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { if (ok) { break; } - if (ctxt.getType() == ExtensionContextType.ELEMENT) { - String en = ctxt.getExpression(); - String pu = null; - if (en == null) { - // nothing? It's an error in the extension definition, but that's properly reported elsewhere - } else { - contexts.append("e:" + en); - if (en.contains("#")) { - pu = en.substring(0, en.indexOf("#")); - en = en.substring(en.indexOf("#")+1); + boolean cok = true; + if (ctxt.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE)) { + Extension ext = ctxt.getExtensionByUrl(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE); + vv = true; + if (ext.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_START)) { + String v = ToolingExtensions.readStringExtension(ext, ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_START); + cok = VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) >= 0 && cok; + } + if (ext.hasExtension(ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_END)) { + String v = ToolingExtensions.readStringExtension(ext, ToolingExtensions.EXT_FHIRVERSION_SPECIFIC_USE_END); + cok = VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) <= 0 && cok; + } + } + if (cok) { + if (ctxt.getType() == ExtensionContextType.ELEMENT) { + String en = ctxt.getExpression(); + String pu = null; + if (en == null) { + // nothing? It's an error in the extension definition, but that's properly reported elsewhere } else { - //pu = en; - } - if (Utilities.existsInList(en, "Element", "Any")) { - ok = true; - } else if (en.equals("Resource") && container.isResource()) { - ok = true; - } else if (en.equals("CanonicalResource") && containsAny(VersionUtilities.getExtendedCanonicalResourceNames(context.getVersion()), plist)) { - ok = true; - } else if (hasElementName(plist, en) && pu == null) { - ok = true; + contexts.append("e:" + en); + if (en.contains("#")) { + pu = en.substring(0, en.indexOf("#")); + en = en.substring(en.indexOf("#")+1); + } else { + //pu = en; + } + if (Utilities.existsInList(en, "Element", "Any")) { + ok = true; + } else if (en.equals("Resource") && container.isResource()) { + ok = true; + } else if (en.equals("CanonicalResource") && containsAny(VersionUtilities.getExtendedCanonicalResourceNames(context.getVersion()), plist)) { + ok = true; + } else if (hasElementName(plist, en) && pu == null) { + ok = true; + } } - } - - if (!ok) { - if (checkConformsToProfile(appContext, errors, resource, container, stack, extUrl, ctxt.getExpression(), pu)) { - for (String p : plist) { - if (ok) { - break; - } - if (p.equals(en)) { - ok = true; - } else { - String pn = p; - String pt = ""; - if (p.contains(".")) { - pn = p.substring(0, p.indexOf(".")); - pt = p.substring(p.indexOf(".")); + + if (!ok) { + if (checkConformsToProfile(appContext, errors, resource, container, stack, extUrl, ctxt.getExpression(), pu)) { + for (String p : plist) { + if (ok) { + break; } - StructureDefinition sd = context.fetchTypeDefinition(pn); - while (sd != null) { - if ((sd.getType() + pt).equals(en)) { - ok = true; - break; + if (p.equals(en)) { + ok = true; + } else { + String pn = p; + String pt = ""; + if (p.contains(".")) { + pn = p.substring(0, p.indexOf(".")); + pt = p.substring(p.indexOf(".")); } - if (sd.getBaseDefinition() != null) { - sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); - } else { - sd = null; + StructureDefinition sd = context.fetchTypeDefinition(pn); + while (sd != null) { + if ((sd.getType() + pt).equals(en)) { + ok = true; + break; + } + if (sd.getBaseDefinition() != null) { + sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); + } else { + sd = null; + } } } } } } - } - } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { - contexts.append("x:" + ctxt.getExpression()); - String ext = null; - if (stack.getElement().getName().startsWith("value")) { - NodeStack estack = stack.getParent(); - if (estack != null && estack.getElement().fhirType().equals("Extension")) { - ext = estack.getElement().getNamedChildValue("url", false); + } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { + contexts.append("x:" + ctxt.getExpression()); + String ext = null; + if (stack.getElement().getName().startsWith("value")) { + NodeStack estack = stack.getParent(); + if (estack != null && estack.getElement().fhirType().equals("Extension")) { + ext = estack.getElement().getNamedChildValue("url", false); + } + } else { + ext = stack.getElement().getNamedChildValue("url", false); + } + if (ctxt.getExpression().equals(ext)) { + ok = true; + } else if (ext != null) { + plist.add(ext); + } + } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { + contexts.append("p:" + ctxt.getExpression()); + // The context is all elements that match the FHIRPath query found in the expression. + List res = fpe.evaluate(valContext, resource, valContext.getRootResource(), resource, fpe.parse(ctxt.getExpression())); + if (res.contains(container)) { + ok = true; } } else { - ext = stack.getElement().getNamedChildValue("url", false); - } - if (ctxt.getExpression().equals(ext)) { - ok = true; - } else if (ext != null) { - plist.add(ext); + throw new Error(context.formatMessage(I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue())); } - } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { - contexts.append("p:" + ctxt.getExpression()); - // The context is all elements that match the FHIRPath query found in the expression. - List res = fpe.evaluate(valContext, resource, valContext.getRootResource(), resource, fpe.parse(ctxt.getExpression())); - if (res.contains(container)) { - ok = true; - } - } else { - throw new Error(context.formatMessage(I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue())); } } if (!ok) { if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) { warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER)); + } else if (vv) { + rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, + modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_VER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_VER, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER), context.getVersion()); } else { rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER)); @@ -6871,7 +6891,7 @@ public boolean checkChildByDefinition(ValidationContext valContext, List Date: Thu, 9 Jan 2025 08:12:17 +1100 Subject: [PATCH 08/13] report count of tests in output from TxTester --- .../hl7/fhir/validation/special/TxTester.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java index 35fcbef55b..4286573151 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java @@ -49,10 +49,25 @@ import org.hl7.fhir.utilities.json.model.JsonObject; import org.hl7.fhir.utilities.json.parser.JsonParser; import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.validation.special.TxTester.IntHolder; public class TxTester { + public class IntHolder { + + private int count; + + public void count() { + count++; + } + + public int total() { + return count; + } + + } + public interface ITxTesterLoader { public String describe(); public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException; @@ -107,6 +122,7 @@ public boolean execute(List modes, String filter) throws IOException, UR System.out.println(" Filter Parameter: "+filter); } + IntHolder counter = new IntHolder(); JsonObject json = new JsonObject(); json.add("date", new SimpleDateFormat("EEE, MMM d, yyyy HH:mmZ", new Locale("en", "US")).format(Calendar.getInstance().getTime()) + timezone()); try { @@ -118,7 +134,7 @@ public boolean execute(List modes, String filter) throws IOException, UR if (suite.asBoolean("disabled")) { // ok = true; } else { - ok = runSuite(suite, modes, filter, json.forceArray("suites")) && ok; + ok = runSuite(suite, modes, filter, json.forceArray("suites"), counter) && ok; } } } @@ -127,10 +143,10 @@ public boolean execute(List modes, String filter) throws IOException, UR if (filter == null) { String m = modes.isEmpty() ? "[none]" : CommaSeparatedStringBuilder.join(";", modes); if (ok) { - System.out.println(software+" passed all HL7 terminology service tests ("+Utilities.pluralize("mode", modes.size())+" "+m+", tests v"+loadVersion()+", runner v"+VersionUtil.getBaseVersion()+")"); + System.out.println(software+" passed all "+counter.total()+" HL7 terminology service tests ("+Utilities.pluralize("mode", modes.size())+" "+m+", tests v"+loadVersion()+", runner v"+VersionUtil.getBaseVersion()+")"); return true; } else { - System.out.println(software+" did not pass all HL7 terminology service tests ("+Utilities.pluralize("mode", modes.size())+" "+m+", tests v"+loadVersion()+", runner v"+VersionUtil.getBaseVersion()+")"); + System.out.println(software+" did not pass all "+counter.total()+" HL7 terminology service tests ("+Utilities.pluralize("mode", modes.size())+" "+m+", tests v"+loadVersion()+", runner v"+VersionUtil.getBaseVersion()+")"); System.out.println("Failed Tests: "+ CommaSeparatedStringBuilder.join(",", fails )); return false; } @@ -200,14 +216,14 @@ public String executeTest(JsonObject suite, JsonObject test, List modes) } List setup = loadSetupResources(suite); - if (runTest(suite, test, setup, modes, "*", null)) { + if (runTest(suite, test, setup, modes, "*", null, new IntHolder())) { return null; } else { return error; } } - private boolean runSuite(JsonObject suite, List modes, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException { + private boolean runSuite(JsonObject suite, List modes, String filter, JsonArray output, IntHolder counter) throws FHIRFormatError, FileNotFoundException, IOException { System.out.println("Group "+suite.asString("name")); JsonObject outputS = new JsonObject(); if (output != null) { @@ -221,14 +237,14 @@ private boolean runSuite(JsonObject suite, List modes, String filter, Js if (test.asBoolean("disabled")) { ok = true; } else { - ok = runTest(suite, test, setup, modes, filter, outputS.forceArray("tests")) && ok; + ok = runTest(suite, test, setup, modes, filter, outputS.forceArray("tests"), counter) && ok; } } } return ok; } - private boolean runTest(JsonObject suite, JsonObject test, List setup, List modes, String filter, JsonArray output) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException { + private boolean runTest(JsonObject suite, JsonObject test, List setup, List modes, String filter, JsonArray output, IntHolder counter) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException { JsonObject outputT = new JsonObject(); if (output != null) { output.add(outputT); @@ -240,6 +256,7 @@ private boolean runTest(JsonObject suite, JsonObject test, List setup, System.out.print(" Test "+test.asString("name")+": "); HTTPHeader header = null; try { + counter.count(); if (test.has("header")) { JsonObject hdr = test.getJsonObject("header"); if (hdr.has("mode") && modes.contains(hdr.asString("mode"))) { From 6b2d433fd442562c55b1656421856a2f56e05c1f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 08:12:25 +1100 Subject: [PATCH 09/13] missed code --- .../main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java index ddfe1d6746..ab5182eecd 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java @@ -1,5 +1,7 @@ package org.hl7.fhir.r5.utils; +import java.util.Collections; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -183,12 +185,15 @@ public static String listUrls(List list) { return b.toString(); } - public static String listStrings(Set set) { + public static String listStrings(Set set, boolean sort) { List list = Utilities.sorted(set); CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (String s : list) { b.append(s); } + if (sort) { + Collections.sort(list); + } return b.toString(); } From a0a6e1b65a62ef05fcd3564356ce6515494c2e97 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 17:02:41 +1100 Subject: [PATCH 10/13] Make sure all validation messages have a message id --- .../java/org/hl7/fhir/r5/elementmodel/Element.java | 8 ++++---- .../org/hl7/fhir/utilities/i18n/I18nConstants.java | 8 ++++++++ .../utilities/validation/ValidationMessage.java | 3 ++- .../org/hl7/fhir/validation/BaseValidator.java | 14 +++++++------- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index 0a5eb5ce80..4e01d9946a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java @@ -1145,12 +1145,12 @@ public List getExtensions(String url) { return list; } - public Base getExtensionValue(String url) { + public Base getExtensionValue(String... url) { if (children != null) { for (Element child : children) { if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { String u = child.getChildValue("url"); - if (url.equals(u)) { + if (Utilities.existsInList(u, url)) { return child.getNamedChild("value", false); } } @@ -1159,12 +1159,12 @@ public Base getExtensionValue(String url) { return null; } - public boolean hasExtension(String url) { + public boolean hasExtension(String... url) { if (children != null) { for (Element child : children) { if (Utilities.existsInList(child.getName(), "extension", "modifierExtension")) { String u = child.getChildValue("url"); - if (url.equals(u)) { + if (Utilities.existsInList(u, url)) { return true; } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index c6c8f05b4d..b45a31e437 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -1174,4 +1174,12 @@ public class I18nConstants { public static final String VALUESET_INCLUDE_WRONG_VS = "VALUESET_INCLUDE_WRONG_VS"; public static final String VALUESET_INCLUDE_WRONG_VS_HINT = "VALUESET_INCLUDE_WRONG_VS_HINT"; public static final String VALUESET_INCLUDE_WRONG_VS_MANY = "VALUESET_INCLUDE_WRONG_VS_MANY"; + public static final String CODESYSTEM_PROPERTY_URI_UNKNOWN = "CODESYSTEM_PROPERTY_URI_UNKNOWN"; + public static final String CODESYSTEM_PROPERTY_URI_INVALID = "CODESYSTEM_PROPERTY_URI_INVALID"; + public static final String CODESYSTEM_PROPERTY_URI_UNKNOWN_BASE = "CODESYSTEM_PROPERTY_URI_UNKNOWN_BASE"; + public static final String CODESYSTEM_PROPERTY_URI_UNKNOWN_TYPE = "CODESYSTEM_PROPERTY_URI_UNKNOWN_TYPE"; + public static final String CODESYSTEM_PROPERTY_CODE_DEFAULT_WARNING = "CODESYSTEM_PROPERTY_CODE_DEFAULT_WARNING"; + public static final String CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND = "CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND"; + public static final String CODESYSTEM_PROPERTY_BAD_INTERNAL_REFERENCE = "CODESYSTEM_PROPERTY_BAD_INTERNAL_REFERENCE"; + public static final String CODESYSTEM_PROPERTY_BAD_PROPERTY_CODE = "CODESYSTEM_PROPERTY_BAD_PROPERTY_CODE"; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java index 330489bab9..1cae571697 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationMessage.java @@ -935,8 +935,9 @@ public String getInvId() { return invId; } - public void setInvId(String invId) { + public ValidationMessage setInvId(String invId) { this.invId = invId; + return this; } public String getComment() { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 5e76f9a566..d9ea37a77a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -327,10 +327,10 @@ protected boolean hint(List errors, String ruleDate, IssueTyp * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ - protected boolean hintInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId) { + protected boolean hintInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId, String msgId) { if (!thePass && doingHints() && !isSuppressedValidationMessage(path, invId)) { String message = context.formatMessage(msg); - addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, msg).setInvId(invId); + addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.INFORMATION, msg).setInvId(invId).setMessageId(msgId); } return thePass; } @@ -441,10 +441,10 @@ protected boolean rule(List errors, String ruleDate, IssueTyp return thePass; } - protected boolean ruleInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, String invId, Object... theMessageArguments) { + protected boolean ruleInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, String invId, String msgId, Object... theMessageArguments) { if (!thePass && doingErrors() && !isSuppressedValidationMessage(path, theMessage)) { String message = context.formatMessage(theMessage, theMessageArguments); - addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, invId).setInvId(invId); + addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, invId).setInvId(invId).setMessageId(msgId); } return thePass; } @@ -619,12 +619,12 @@ protected boolean warning(List errors, String ruleDate, Issue } - protected boolean warningInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId, Object... theMessageArguments) { + protected boolean warningInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId, String msgId, Object... theMessageArguments) { if (!thePass && doingWarnings() && !isSuppressedValidationMessage(path, invId)) { String nmsg = context.formatMessage(msg, theMessageArguments); IssueSeverity severity = IssueSeverity.WARNING; String id = idForMessage(msg, nmsg); - addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id).setMessageId(id).setInvId(invId); + addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id).setMessageId(msgId).setInvId(invId); } return thePass; @@ -807,7 +807,7 @@ protected boolean warning(List errors, String ruleDate, Issue protected boolean warning(List errors, String ruleDate, IssueType type, String path, boolean thePass, String msg, Object... theMessageArguments) { if (!thePass && doingWarnings() && !isSuppressedValidationMessage(path, msg)) { String message = context.formatMessage(msg, theMessageArguments); - addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, null); + addValidationMessage(errors, ruleDate, type, -1, -1, path, message, IssueSeverity.WARNING, msg); } return thePass; } From 8f6f75f4bf392ed6c187a45a6a9035a9261e537f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 17:08:28 +1100 Subject: [PATCH 11/13] beef up validation of CodeSystem properties that are codes --- .../src/main/resources/Messages.properties | 8 ++ .../instance/InstanceValidator.java | 15 ++- .../instance/type/CodeSystemValidator.java | 124 ++++++++++++++++-- .../4.0.1/vs-externals.json | 19 ++- .../5.0.0/vs-externals.json | 4 + 5 files changed, 147 insertions(+), 23 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 179613c3ae..ee6a0e482b 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1207,3 +1207,11 @@ EXTENSION_FHIR_VERSION_LATEST = The definition of the extension ''{0}'' specifie VALUESET_INCLUDE_WRONG_VS = The system ''{0}'' is actually a value set VALUESET_INCLUDE_WRONG_VS_HINT = The system ''{0}'' is actually a value set, which itself refers to the system ''{1}'' so that may be what is intended here VALUESET_INCLUDE_WRONG_VS_MANY = The system ''{0}'' is actually a value set, which itself refers to the systems {1} +CODESYSTEM_PROPERTY_URI_UNKNOWN = The property uri ''{0}'' is not known, so the property values can not be validated fully +CODESYSTEM_PROPERTY_URI_UNKNOWN_TYPE = The property uri ''{0}'' is not known, so the property values can not be validated fully; unless specified elsewhere, codes will be treated as internal references +CODESYSTEM_PROPERTY_URI_UNKNOWN_BASE = The base property uri ''{0}'' is not known, so the property values can not be validated fully +CODESYSTEM_PROPERTY_URI_INVALID = The code ''{0}'' in the CodeSystem {2} for uri ''{1}'' is not valid +CODESYSTEM_PROPERTY_CODE_DEFAULT_WARNING = The type is ''code'', but no ValueSet information was found, so the codes will be validated as internal codes +CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND = The ValueSet {0} is unknown, so the property codes cannot be validated +CODESYSTEM_PROPERTY_BAD_INTERNAL_REFERENCE = The code ''{0}'' is not a valid code in this code system +CODESYSTEM_PROPERTY_BAD_PROPERTY_CODE = The code ''{0}'' is not a valid code in the value set ''{1}'' diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index b333d93972..f87dcfa033 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -7503,9 +7503,12 @@ public boolean checkInvariant(ValidationContext valContext, List propertyCodes = new HashSet(); public CodeSystemValidator(BaseValidator parent) { super(parent); @@ -237,11 +246,48 @@ private boolean checkPropertyDefinition(List errors, Element PropertyDef pd = new PropertyDef(uri, code, type); KnownProperty ukp = null; KnownProperty ckp = null; + boolean foundPropDefn = false; + CodeValidationRule ruleFromUri = CodeValidationRule.INTERNAL_CODE_WARNING; + String valuesetFromUri = null; + if (uri != null) { if (rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), Utilities.isAbsoluteUrl(uri), I18nConstants.CODESYSTEM_PROPERTY_ABSOLUTE_URI, uri)) { if (rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), !properties.containsKey(uri), I18nConstants.CODESYSTEM_PROPERTY_DUPLICATE_URI, uri)) { properties.put(uri, pd); + if (uri.contains("#")) { + String base = uri.substring(0, uri.indexOf("#")); + String pcode = uri.substring(uri.indexOf("#")+1); + CodeSystem pcs = context.findTxResource(CodeSystem.class, base); + if (pcs == null) { + warning(errors, "2025-01-09", IssueType.NOTFOUND, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_URI_UNKNOWN_BASE, base); + } else { + ConceptDefinitionComponent cc = CodeSystemUtilities.findCode(pcs.getConcept(), pcode); + if (rule(errors, "2025-01-09", IssueType.INVALID, cs.line(), cs.col(), stack.getLiteralPath(), cc != null, I18nConstants.CODESYSTEM_PROPERTY_URI_INVALID, pcode, base, pcs.present())) { + foundPropDefn = true; + if ("code".equals(type)) { + ConceptPropertyComponent ccp = CodeSystemUtilities.getProperty(cc, "binding"); + if (ccp != null && ccp.hasValue() && ccp.getValue().hasPrimitiveValue()) { + ruleFromUri = CodeValidationRule.VS_ERROR; + valuesetFromUri = ccp.getValue().primitiveValue(); + } else { + ruleFromUri = CodeValidationRule.INTERNAL_CODE_WARNING; + } + } + } else { + ok = false; + if ("code".equals(type)) { + ruleFromUri = CodeValidationRule.INTERNAL_CODE_WARNING; + } + } + } + } else { + if ("code".equals(type)) { + warning(errors, "2025-01-09", IssueType.NOTFOUND, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_URI_UNKNOWN_TYPE, uri); + } else { + hint(errors, "2025-01-09", IssueType.NOTFOUND, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_URI_UNKNOWN, uri); + } + } } else { ok = false; } @@ -352,21 +398,26 @@ private boolean checkPropertyDefinition(List errors, Element pd.setCodeValidationRules(CodeValidationRule.INTERNAL_CODE, null); break; case Status: - pd.setCodeValidationRules(CodeValidationRule.VS_WARNING, VS_PROP_STATUS); + pd.setCodeValidationRules(CodeValidationRule.VS_WARNING, findVS(errors, cs, stack, VS_PROP_STATUS, I18nConstants.CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND)); break; default: break; } } else if ("code".equals(pd.getType())) { - if (property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) { - pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue()); + if (property.hasExtension("http://hl7.org/fhir/StructureDefinition/codesystem-property-valueset", "http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) { + pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, findVS(errors, cs, stack, + property.getExtensionValue("http://hl7.org/fhir/StructureDefinition/codesystem-property-valueset", "http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue(), + I18nConstants.CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND)); + } else if (foundPropDefn && valuesetFromUri != null) { + pd.setCodeValidationRules(ruleFromUri, findVS(errors, cs, stack, valuesetFromUri, I18nConstants.CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND)); } else if (VersionUtilities.isR6Plus(context.getVersion())) { hint(errors, "2024-03-18", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), ukp != null && type.equals(ukp.getType()), I18nConstants.CODESYSTEM_PROPERTY_CODE_WARNING); } else { - + pd.setCodeValidationRules(ruleFromUri, null); + hint(errors, "2025-01-09", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_PROPERTY_CODE_DEFAULT_WARNING); } - } else if ("Coding".equals(pd.getType()) && property.hasExtension("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) { - pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, property.getExtensionValue("http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue()); + } else if ("Coding".equals(pd.getType()) && property.hasExtension("http://hl7.org/fhir/StructureDefinition/codesystem-property-valueset", "http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet")) { + pd.setCodeValidationRules(CodeValidationRule.VS_ERROR, findVS(errors, cs, stack, property.getExtensionValue("http://hl7.org/fhir/StructureDefinition/codesystem-property-valueset", "http://hl7.org/fhir/6.0/StructureDefinition/extension-CodeSystem.property.valueSet").primitiveValue(), I18nConstants.CODESYSTEM_PROPERTY_VALUESET_NOT_FOUND)); } if (uri == null) { @@ -382,6 +433,18 @@ private boolean checkPropertyDefinition(List errors, Element return ok; } + private ValueSet findVS(List errors, Element cs, NodeStack stack, String url, String message) { + if (url == null) { + return null; + } else { + ValueSet vs = context.findTxResource(ValueSet.class, url); + if (vs != null) { + warning(errors, "2025-01-09", IssueType.NOTFOUND, cs.line(), cs.col(), stack.getLiteralPath(), false, message, url); + } + return vs; + } + } + private boolean isBaseSpec(String url) { return url.startsWith("http://hl7.org/fhir/") && !url.substring(20).contains("/"); } @@ -480,7 +543,9 @@ private boolean checkPropertyValue(List errors, Element cs, N if (rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), defn != null, I18nConstants.CODESYSTEM_PROPERTY_UNDEFINED, code) && rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), value != null, I18nConstants.CODESYSTEM_PROPERTY_NO_VALUE, code) && rule(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), value.fhirType().equals(defn.type), I18nConstants.CODESYSTEM_PROPERTY_WRONG_TYPE, code, value.fhirType(), defn.type)) { - // nothing? + if ("code".equals(value.fhirType())) { + checkCodeProperty(errors, cs, stack, defn, value.primitiveValue(), codes); + } } else { ok = false; } @@ -492,6 +557,43 @@ private boolean checkPropertyValue(List errors, Element cs, N return ok; } + private void checkCodeProperty(List errors, Element cs, NodeStack stack, PropertyDef defn, String code, Set codes) { + switch (defn.getRule()) { + case INTERNAL_CODE: + if (!isSeenPropertyCode(defn, code)) { + rule(errors, "2025-01-09", IssueType.INVALID, cs.line(), cs.col(), stack.getLiteralPath(), codes.contains(code), I18nConstants.CODESYSTEM_PROPERTY_BAD_INTERNAL_REFERENCE, code); + } + break; + case INTERNAL_CODE_WARNING: + if (!isSeenPropertyCode(defn, code)) { + warning(errors, "2025-01-09", IssueType.INVALID, cs.line(), cs.col(), stack.getLiteralPath(), codes.contains(code), I18nConstants.CODESYSTEM_PROPERTY_BAD_INTERNAL_REFERENCE, code); + } + break; + case VS_ERROR: + if (defn.getValueset() != null && !isSeenPropertyCode(defn, code)) { + ValidationResult vo = context.validateCode(baseOptions, code, defn.getValueset()); + rule(errors, "2025-01-09", IssueType.INVALID, cs.line(), cs.col(), stack.getLiteralPath(), vo.isOk(), I18nConstants.CODESYSTEM_PROPERTY_BAD_PROPERTY_CODE, code); + } + break; + case VS_WARNING: + if (defn.getValueset() != null && !isSeenPropertyCode(defn, code)) { + ValidationResult vo = context.validateCode(baseOptions, code, defn.getValueset()); + warning(errors, "2025-01-09", IssueType.INVALID, cs.line(), cs.col(), stack.getLiteralPath(), vo.isOk(), I18nConstants.CODESYSTEM_PROPERTY_BAD_PROPERTY_CODE, code, defn.getValueset().getVersionedUrl()); + } + break; + default: + case NO_VALIDATION: + break; + } + } + + private boolean isSeenPropertyCode(PropertyDef defn, String code) { + String key = defn.getValueset() != null ? defn.getValueset().getVersionedUrl()+"#"+code : "null#"+code; + boolean isnew = propertyCodes.contains(key); + propertyCodes.add(key); + return isnew; + } + private boolean checkShareableCodeSystem(List errors, Element cs, NodeStack stack) { if (parent.isForPublication()) { if (isHL7(cs)) { diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/vs-externals.json b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/vs-externals.json index 62253e09ae..bfc24d8e9d 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/vs-externals.json +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/vs-externals.json @@ -1,17 +1,24 @@ { - "http://fhir.abda.de/eRezeptAbgabedaten/ValueSet/DAV-VS-ERP-DEUEV-Anlage-8" : null, - "http://loinc.org/vs/LL378-1" : null, + "http://loinc.org|2.76" : null, "http://www.rfc-editor.org/bcp/bcp13.txt" : null, - "http://loinc.org/vs/LL1971-2" : null, + "#f3b2bd36-199b-4591-b4db-f49db0912b6|null" : null, + "#c1|null" : null, "http://hl7.org/fhir/us/davinci-hrex/ValueSet/hrex-endpoint-name" : null, "http://fhir.ch/ig/ch-ig/ValueSet/ch-ig-example" : null, "http://hl7.org/fhir/us/qicore/ValueSet/qicore-negation-reason" : null, + "http://fhir.ch/ig/ch-ig/ValueSet/OrganizationType" : null, + "https://fhir.kbv.de/ValueSet/KBV_VS_SFHIR_ICD_SEITENLOKALISATION" : null, + "http://fhir.abda.de/eRezeptAbgabedaten/ValueSet/DAV-VS-ERP-DEUEV-Anlage-8" : null, + "http://loinc.org/vs/LL378-1" : null, + "http://something/something|null" : null, + "http://hl7.org/fhir/CodeSystem/c1|null" : null, + "http://hl7.org/fhir/uv/sdc/CodeSystem/CSPHQ9|null" : null, + "http://loinc.org/vs/LL1971-2" : null, + "#f3b2bd36-199b-4591-b4db-f49db0912b62|null" : null, "http://hl7.org/fhir/smart-app-launch/ValueSet/user-access-category" : null, "https://healthterminologies.gov.au/fhir/ValueSet/australian-immunisation-register-vaccine-1" : { "server" : "https://tx.ontoserver.csiro.au/fhir", "filename" : "vs-0ce1569f-f985-44cb-a5a0-9d205efd76b4.json" }, - "http://fhir.ch/ig/ch-ig/ValueSet/OrganizationType" : null, - "http://loinc.org/vs/LL4048-6" : null, - "https://fhir.kbv.de/ValueSet/KBV_VS_SFHIR_ICD_SEITENLOKALISATION" : null + "http://loinc.org/vs/LL4048-6" : null } diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/vs-externals.json b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/vs-externals.json index c09095f3ec..7a3a08b987 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/vs-externals.json +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/vs-externals.json @@ -1,8 +1,12 @@ { "http://hl7.org/fhir/ValueSet/ucum-vitals-common|4.0.0" : null, "http://loinc.org/vs" : null, + "#cs1|null" : null, + "#cs|null" : null, "http://hl7.org.au/fhir/ValueSet/au-hl7v2-0203" : null, "http://hl7.org/fhir/ValueSet/name-use|4.0.1" : null, + "http://something|null" : null, + "http://snomed.info/sct|null" : null, "http://hl7.org/fhir/ValueSet/observation-status|4.0.0" : null, "http://hl7.org/fhir/ValueSet/identifier-use|4.0.1" : null, "http://snomed.info/sct?fhir_vs" : null From aa5edab2ab0f2fbbe01b76228e1b328c18d6fd40 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 17:08:51 +1100 Subject: [PATCH 12/13] Do not create issue about draft dependency for example bindings --- .../instance/type/StructureDefinitionValidator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 13c44d27ae..caee273df9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -1140,7 +1140,9 @@ private boolean validateBinding(List errors, Element binding, if (vs != null) { if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) { ValueSet vsr = (ValueSet) vs; - warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref); + if (!"example".equals(binding.getNamedChildValue("strength"))) { + warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref); + } } else { ok = false; } From 8046453ec2b5e710c689aae29c6b590a2bb9d8fb Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 9 Jan 2025 17:13:42 +1100 Subject: [PATCH 13/13] release notes --- RELEASE_NOTES.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b06c6ab5f..1c3d48ee55 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,7 +1,20 @@ ## Validator Changes -* no changes +* Do not create issue about draft dependency for example bindings +* Beef up validation of CodeSystem properties that are codes +* Make sure all validation messages have a message id +* Validator enforce version-set-specific value for Extension and Extension context +* Specific Error when ValueSet.compose.include.system refers to a ValueSet ## Other code changes -* no changes \ No newline at end of file +* Report count of tests in output from TxTester +* resolve issues with references between IGs to example resources +* Lookup compliesWithProfile target from link-only dependencies +* Update SNOMED editions related routines (add more editions) +* Accessibility - role=presentation on appropriate tables +* Add support for ADL in packages +* Support for Archetype processing in IG publisher +* Lazy load binaries for reduced memory usage + +