diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index f086f9f8b..16b89d10d 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 940c2a372..dad77acac 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 7a5784e6a..fabefb898 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 2573f8d0a..821aa266a 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/conformance/ProfileUtilities.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/conformance/ProfileUtilities.java index 945274c62..4e1299b53 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/conformance/ProfileUtilities.java @@ -1657,7 +1657,7 @@ public class ProfileUtilities extends TranslatingUtilities { r.getCells().add(gen.new Cell("", "", "Extension", null, null)); r.setIcon("icon_"+m+"extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - + for (ElementDefinition c : children) { ved = getValueFor(ed, c); ElementDefinition ued = getUrlFor(ed, c); @@ -1697,7 +1697,7 @@ public class ProfileUtilities extends TranslatingUtilities { } c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null)); r.getCells().add(c); - + try { return gen.generate(model, corePath, 0, outputTracker); } catch (org.hl7.fhir.exceptions.FHIRException e) { @@ -3484,7 +3484,7 @@ public class ProfileUtilities extends TranslatingUtilities { TableModel model = initSpanningTable(gen, "", false, profile.getId()); Set processed = new HashSet(); SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); - + genSpanEntry(gen, model.getRows(), span); return gen.generate(model, "", 0, outputTracker); } diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/NarrativeGenerator.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/NarrativeGenerator.java index 35bb777f9..8f5ea5d16 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/NarrativeGenerator.java @@ -1,33 +1,33 @@ package org.hl7.fhir.dstu3.utils; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -3236,7 +3236,7 @@ public class NarrativeGenerator implements INarrativeGenerator { case INEXACT : return "><"; case UNMATCHED : return "-"; case DISJOINT : return "!="; - case NULL: return null; + case NULL: return null; default: return "?"; } } @@ -3708,6 +3708,7 @@ public class NarrativeGenerator implements INarrativeGenerator { inject(sd, x, NarrativeStatus.GENERATED); return true; } + public boolean generate(ResourceContext rcontext, ImplementationGuide ig) throws EOperationOutcome, FHIRException, IOException { XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); x.h2().addText(ig.getName()); diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 8854f1e69..9e9d94405 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4b/pom.xml b/org.hl7.fhir.r4b/pom.xml index 33c1f5998..998e4c8c8 100644 --- a/org.hl7.fhir.r4b/pom.xml +++ b/org.hl7.fhir.r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index ccb771afc..6e3bb6193 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java index 22f829295..1356ffae7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java @@ -1253,7 +1253,7 @@ public class StructureDefinitionComparer extends CanonicalResourceComparer imple public XhtmlNode renderStructure(ProfileComparison comp, String id, String prefix, String corePath) throws FHIRException, IOException { HierarchicalTableGenerator gen = new HierarchicalTableGenerator(session.getI18n(), Utilities.path("[tmp]", "compare"), false, true); TableModel model = gen.initComparisonTable(corePath, id); - genElementComp(null /* come back to this later */, gen, model.getRows(), comp.combined, corePath, prefix, null, true); + genElementComp(null /* come back to this later */, null /* come back to this later */, gen, model.getRows(), comp.combined, corePath, prefix, null, true); return gen.generate(model, prefix, 0, null); } @@ -1268,7 +1268,7 @@ public class StructureDefinitionComparer extends CanonicalResourceComparer imple return sdr.generateTable(corePath, comp.intersection, false, prefix, false, id, true, corePath, prefix, false, true, null, false, sdr.getContext(), "i"); } - private void genElementComp(String defPath, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { + private void genElementComp(String defPath, String anchorPrefix, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { Row originalRow = slicingRow; Row typesRow = null; @@ -1334,19 +1334,19 @@ public class StructureDefinitionComparer extends CanonicalResourceComparer imple nc = sdrRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName, null); } if (combined.hasLeft()) { - frame(sdrLeft.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, sdrLeft.getContext(), children.size() > 0), leftColor); + frame(sdrLeft.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false, sdrLeft.getContext(), children.size() > 0, defPath, anchorPrefix, new ArrayList()), leftColor); } else { frame(spacers(row, 4, gen), leftColor); } if (combined.hasRight()) { - frame(sdrRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false, sdrRight.getContext(), children.size() > 0), rightColor); + frame(sdrRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false, sdrRight.getContext(), children.size() > 0, defPath, anchorPrefix, new ArrayList()), rightColor); } else { frame(spacers(row, 4, gen), rightColor); } row.getCells().add(cellForMessages(gen, combined.getMessages())); for (StructuralMatch child : children) { - genElementComp(defPath, gen, row.getSubRows(), child, corePath, prefix, originalRow, false); + genElementComp(defPath, anchorPrefix, gen, row.getSubRows(), child, corePath, prefix, originalRow, false); } } 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 c671d79e5..5fcb7646f 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 @@ -611,18 +611,24 @@ public class Element extends Base implements NamedItem { } - @Override - public boolean hasPrimitiveValue() { - return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); - } - + @Override + public boolean hasPrimitiveValue() { + //return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); + return super.hasPrimitiveValue(); + } + + @Override + public boolean canHavePrimitiveValue() { + return property.isPrimitiveName(name) || property.IsLogicalAndHasPrimitiveValue(name); + } + @Override public String primitiveValue() { if (isPrimitive() || value != null) return value; else { - if (hasPrimitiveValue() && children != null) { + if (canHavePrimitiveValue() && children != null) { for (Element c : children) { if (c.getName().equals("value")) return c.primitiveValue(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java index 0e105150a..6a4b08ece 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java @@ -258,32 +258,70 @@ public abstract class Base implements Serializable, IBase, IElement { } // these 3 allow evaluation engines to get access to primitive values + + /** + * @return true if the data type is a primitive type and might have a primitive value + * (which will be accessed as a string, irrespective of the stated value) + */ public boolean isPrimitive() { return false; } + /** + * @return true if the type is boolean, and the primitive value can only be 'true' or 'false' + */ public boolean isBooleanPrimitive() { return false; } - public boolean hasPrimitiveValue() { - return isPrimitive(); - } - + /** + * @return true if the type is primitive, and there's value (e.g. no Data-Absent-Reason extension etc) + */ + public boolean hasPrimitiveValue() { + return primitiveValue() != null; + } + + /** + * @return true if the type is primitive, and there could be a value (irrespective of whether it's present e.g. no Data-Absent-Reason extension etc) + */ + public boolean canHavePrimitiveValue() { + return false; + } + + /** + * @return the primitive value if there is one, as a string irrespective of the actual type (e.g. dates converted to their FHIR string representation) + * return null if the value is not a primitive or there is no value (might be extensions instead) + */ public String primitiveValue() { return null; } + /** + * @return true if the type is date|dateTime|instant, and the primitive value is a date/time of some precision + */ public boolean isDateTime() { return false; } + /** + * @return the date/time value if there is one, or null + */ public BaseDateTimeType dateTimeValue() { return null; } - + + /** + * @return the FHIR type name of the instance (not the java class name) + */ public abstract String fhirType() ; - + + /** + * Note that this is potentially misleading on ElementDefinition that has a 'type' + * property - don't mistakenly use this thinking it's going to look at ElementDefinition.type + * + * @param name - fhir type name + * @return- true if it 'has' this type (including by specialization) + */ public boolean hasType(String... name) { String t = fhirType(); for (String n : name) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java index 9662eaceb..6b7926f00 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java @@ -257,6 +257,11 @@ public abstract class PrimitiveType extends DataType implements IPrimitiveTyp public boolean hasPrimitiveValue() { return StringUtils.isNotBlank(getValueAsString()); } + + public boolean canHavePrimitiveValue() { + return true; + } + public String fpValue() { return primitiveValue(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java index 39f5cd67d..dc40ca976 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Set; +import org.fhir.ucum.Canonical; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.conformance.profile.BindingResolution; @@ -14,6 +15,7 @@ import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ActorDefinition; +import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ElementDefinition; @@ -40,9 +42,9 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNodeList; public class ObligationsRenderer { public static class ObligationDetail { - private String code; + private List codes = new ArrayList<>(); private List elementIds = new ArrayList<>(); - private String actor; + private List actors = new ArrayList<>(); private String doco; private String docoShort; private String filter; @@ -57,10 +59,11 @@ public class ObligationsRenderer { private int count = 1; public ObligationDetail(Extension ext) { - this.code = ext.getExtensionString("code"); - this.actor = ext.getExtensionString("actor"); - if (this.actor == null) { - this.actor = ext.getExtensionString("actorId"); + for (Extension e: ext.getExtensionsByUrl("code")) { + codes.add(e.getValueStringType().toString()); + } + for (Extension e: ext.getExtensionsByUrl("actor")) { + actors.add(e.getValueCanonicalType()); } this.doco = ext.getExtensionString("documentation"); this.docoShort = ext.getExtensionString("shortDoco"); @@ -80,7 +83,7 @@ public class ObligationsRenderer { private String getKey() { // Todo: Consider extending this with content from usageContext if purpose isn't sufficiently differentiating - return code + Integer.toString(count); + return String.join(",", codes) + Integer.toString(count); } private void incrementCount() { @@ -96,8 +99,11 @@ public class ObligationsRenderer { public String getDoco(boolean full) { return full ? doco : docoShort; } - public String getCode() { - return code; + public String getCodes() { + return String.join(",", codes); + } + public List getCodeList() { + return new ArrayList(codes); } public boolean unchanged() { if (!isUnchanged) @@ -105,9 +111,9 @@ public class ObligationsRenderer { if (compare==null) return true; isUnchanged = true; - isUnchanged = isUnchanged && ((code==null && compare.code==null) || code.equals(compare.code)); + isUnchanged = isUnchanged && ((codes.isEmpty() && compare.codes.isEmpty()) || codes.equals(compare.codes)); isUnchanged = elementIds.equals(compare.elementIds); - isUnchanged = isUnchanged && ((actor==null && compare.actor==null) || actor.equals(compare.actor)); + isUnchanged = isUnchanged && ((actors.isEmpty() && compare.actors.isEmpty()) || actors.equals(compare.actors)); isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco)); isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort)); isUnchanged = isUnchanged && ((filter==null && compare.filter==null) || filter.equals(compare.filter)); @@ -136,12 +142,16 @@ public class ObligationsRenderer { return usage; } - public boolean hasActor() { - return actor != null; + public boolean hasActors() { + return !actors.isEmpty(); } public boolean hasActor(String id) { - return id.equals(actor); + for (CanonicalType actor: actors) { + if (actor.getValue().equals(id)) + return true; + } + return false; } } @@ -276,24 +286,24 @@ public class ObligationsRenderer { return abr; } - public String render() throws IOException { + public String render(String defPath, String anchorPrefix, List inScopeElements) throws IOException { if (obligations.isEmpty()) { return ""; } else { XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table"); tbl.attribute("class", "grid"); - renderTable(tbl.getChildNodes(), true); + renderTable(tbl.getChildNodes(), true, defPath, anchorPrefix, inScopeElements); return new XhtmlComposer(false).compose(tbl); } } - public void renderTable(HierarchicalTableGenerator gen, Cell c) throws FHIRFormatError, DefinitionException, IOException { + public void renderTable(HierarchicalTableGenerator gen, Cell c, List inScopeElements) throws FHIRFormatError, DefinitionException, IOException { if (obligations.isEmpty()) { return; } else { Piece piece = gen.new Piece("table").attr("class", "grid"); c.getPieces().add(piece); - renderTable(piece.getChildren(), false); + renderTable(piece.getChildren(), false, gen.getDefPath(), gen.getAnchorPrefix(), inScopeElements); } } @@ -313,10 +323,21 @@ public class ObligationsRenderer { } private void renderObligationLI(XhtmlNodeList children, ObligationDetail ob) throws IOException { - renderCode(children, ob.getCode()); - if (ob.hasFilter() || ob.hasUsage()) { + renderCodes(children, ob.getCodeList()); + if (ob.hasFilter() || ob.hasUsage() || !ob.elementIds.isEmpty()) { children.tx(" ("); boolean ffirst = !ob.hasFilter(); + boolean firstEid = true; + + for (String eid: ob.elementIds) { + if (firstEid) { + children.span().i().tx("Elements: "); + firstEid = false; + } else + children.tx(", "); + String trimmedElement = eid.substring(eid.indexOf(".")+ 1); + children.tx(trimmedElement); + } if (ob.hasFilter()) { children.span(null, ob.getFilterDesc()).code().tx(ob.getFilter()); } @@ -337,20 +358,25 @@ public class ObligationsRenderer { } - public void renderTable(List children, boolean fullDoco) throws FHIRFormatError, DefinitionException, IOException { + public void renderTable(List children, boolean fullDoco, String defPath, String anchorPrefix, List inScopeElements) throws FHIRFormatError, DefinitionException, IOException { boolean doco = false; boolean usage = false; boolean actor = false; boolean filter = false; boolean elementId = false; for (ObligationDetail binding : obligations) { - actor = actor || binding.actor!=null || (binding.compare!=null && binding.compare.actor !=null); + actor = actor || !binding.actors.isEmpty() || (binding.compare!=null && !binding.compare.actors.isEmpty()); doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null); usage = usage || !binding.usage.isEmpty() || (binding.compare!=null && !binding.compare.usage.isEmpty()); filter = filter || binding.filter != null || (binding.compare!=null && binding.compare.filter!=null); elementId = elementId || !binding.elementIds.isEmpty() || (binding.compare!=null && !binding.compare.elementIds.isEmpty()); } + List inScopePaths = new ArrayList<>(); + for (ElementDefinition e: inScopeElements) { + inScopePaths.add(e.getPath()); + } + XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr"); children.add(tr); tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_OBLIG)); @@ -379,43 +405,52 @@ public class ObligationsRenderer { children.add(tr); XhtmlNode code = tr.td().style("font-size: 11px"); - if (ob.compare!=null && ob.code.equals(ob.compare.code)) + if (ob.compare!=null && ob.getCodes().equals(ob.compare.getCodes())) code.style("font-color: darkgray"); - renderCode(code.getChildNodes(), ob.code); - if (ob.compare!=null && ob.compare.code != null && !ob.code.equals(ob.compare.code)) { + renderCodes(code.getChildNodes(), ob.getCodeList()); + if (ob.compare!=null && !ob.compare.getCodeList().isEmpty() && !ob.getCodes().equals(ob.compare.getCodes())) { code.br(); code = code.span(STYLE_UNCHANGED, null); - renderCode(code.getChildNodes(), ob.compare.code); + renderCodes(code.getChildNodes(), ob.compare.getCodeList()); } - if (actor) { - ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, ob.actor); - ActorDefinition compAd = null; - if (ob.compare!=null && ob.compare.actor!=null) { - compAd = context.getContext().fetchResource(ActorDefinition.class, ob.compare.actor); + XhtmlNode actorId = tr.td().style("font-size: 11px"); + if (!ob.actors.isEmpty() || ob.compare.actors.isEmpty()) { + boolean firstActor = false; + for (CanonicalType anActor : ob.actors) { + ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, anActor.toString()); + boolean existingActor = ob.compare != null && ob.compare.actors.contains(anActor); + + if (!firstActor) { + actorId.br(); + firstActor = true; + } + + if (!existingActor) + actorId.style(STYLE_UNCHANGED); + } - XhtmlNode actorId = tr.td().style("font-size: 11px"); - if (ob.compare!=null && ob.actor.equals(ob.compare.actor)) - actorId.style(STYLE_UNCHANGED); - if (ad != null && ad.hasWebPath()) { - actorId.ah(ad.getWebPath(), ob.actor).tx(ad.present()); - } else if (ad != null) { - actorId.span(null, ob.actor).tx(ad.present()); - } - - if (ob.compare!=null && ob.compare.actor!=null && !ob.actor.equals(ob.compare.actor)) { - actorId.br(); - actorId = actorId.span(STYLE_REMOVED, null); - if (compAd != null) { - if (compAd.hasWebPath()) { - actorId.ah(compAd.getWebPath(), ob.compare.actor).tx(compAd.present()); - } else { - actorId.span(null, ob.compare.actor).tx(compAd.present()); + if (ob.compare != null) { + for (CanonicalType compActor : ob.compare.actors) { + if (!ob.actors.contains(compActor)) { + ActorDefinition compAd = context.getContext().fetchResource(ActorDefinition.class, compActor.toString()); + if (!firstActor) { + actorId.br(); + firstActor = true; + } + actorId = actorId.span(STYLE_REMOVED, null); + if (compAd.hasWebPath()) { + actorId.ah(compAd.getWebPath(), compActor.toString()).tx(compAd.present()); + } else { + actorId.span(null, compActor.toString()).tx(compAd.present()); + } } } } } + + if (elementId) { XhtmlNode elementIds = tr.td().style("font-size: 11px"); if (ob.compare!=null && ob.elementIds.equals(ob.compare.elementIds)) @@ -423,10 +458,13 @@ public class ObligationsRenderer { for (String eid : ob.elementIds) { elementIds.sep(", "); ElementDefinition ed = profile.getSnapshot().getElementById(eid); - if (ed != null) { - elementIds.ah("#"+eid).tx(ed.getName()); + boolean inScope = inScopePaths.contains(ed.getPath()); + String name = eid.substring(eid.indexOf(".") + 1); + if (ed != null && inScope) { + String link = defPath + "#" + anchorPrefix + eid; + elementIds.ah(link).tx(name); } else { - elementIds.code().tx(eid); + elementIds.code().tx(name); } } @@ -491,10 +529,10 @@ public class ObligationsRenderer { return newS + "
" + oldS + ""; } - private void renderCode(XhtmlNodeList children, String codeExpr) { - if (codeExpr != null) { + private void renderCodes(XhtmlNodeList children, List codes) { + + if (!codes.isEmpty()) { boolean first = true; - String[] codes = codeExpr.split("\\+"); for (String code : codes) { if (first) first = false; else children.tx(" & "); int i = code.indexOf(":"); @@ -507,7 +545,7 @@ public class ObligationsRenderer { CodeResolution cr = this.cr.resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code); code = code.replace("will-", "").replace("can-", ""); if (cr.getLink() != null) { - children.ah(cr.getLink(), cr.getHint()).tx(code); + children.ah(cr.getLink(), cr.getHint()).tx(code); } else { children.span(null, cr.getHint()).tx(code); } 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 976ae6faf..8624f069f 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 @@ -549,7 +549,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean logicalModel, boolean allInvariants, Set outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException { assert(diff != snapshot);// check it's ok to get rid of one of these - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true); + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defFile, anchorPrefix); List list; if (diff) @@ -815,7 +815,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { genElementObligations(gen, element, columns, row, corePath, profile); break; case SUMMARY: - genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc, children.size() > 0); + genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc, children.size() > 0, defPath, anchorPrefix, all); break; } if (element.hasSlicing()) { @@ -1078,7 +1078,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { public List genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, - boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows, RenderingContext rc, boolean walksIntoThis) throws IOException { + boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows, RenderingContext rc, boolean walksIntoThis, String defPath, String anchorPrefix, List inScopeElements) throws IOException { List res = new ArrayList<>(); Cell gc = gen.new Cell(); row.getCells().add(gc); @@ -1121,7 +1121,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (extDefn == null) { res.add(genCardinality(gen, element, row, hasDef, used, null)); res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null))); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements)); } else { String name = element.hasSliceName() ? element.getSliceName() : urltail(eurl); nameCell.getPieces().get(0).setText(name); @@ -1134,7 +1134,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { else // if it's complex, we just call it nothing // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); res.add(addCell(row, gen.new Cell(null, null, "("+(context.formatPhrase(RenderingContext.STRUC_DEF_COMPLEX))+")", null, null))); - res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows, rc)); + res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows, rc, inScopeElements)); } } else { res.add(genCardinality(gen, element, row, hasDef, used, null)); @@ -1142,7 +1142,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { res.add(addCell(row, gen.new Cell())); else res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements)); } } } else if (element != null) { @@ -1151,7 +1151,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); else res.add(addCell(row, gen.new Cell())); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements)); } return res; } @@ -1309,10 +1309,14 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { - return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc); - } - - public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { + return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc, new ArrayList()); + } + + public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc, List inScopeElements) throws IOException, FHIRException { + return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc, inScopeElements); + } + + public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc, List inScopeElements) throws IOException, FHIRException { Cell c = gen.new Cell(); row.getCells().add(c); @@ -1691,7 +1695,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) { obr.seeObligations(profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)); } - obr.renderTable(gen, c); + obr.renderTable(gen, c, inScopeElements); if (definition.hasMaxLength() && definition.getMaxLength()!=0) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } @@ -3046,7 +3050,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { public XhtmlNode generateSpanningTable(StructureDefinition profile, String imageFolder, boolean onlyConstraints, String constraintPrefix, Set outputTracker) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, false, true); + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, false, true, "", ""); TableModel model = initSpanningTable(gen, "", false, profile.getId()); Set processed = new HashSet(); SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); @@ -3149,8 +3153,8 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return first ? null : x; } - public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set outputTracker, RenderingContext rc) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true); + public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set outputTracker, RenderingContext rc, String defPath, String anchorPrefix) throws IOException, FHIRException { + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defPath, anchorPrefix); TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true, TableGenerationMode.XHTML); boolean deep = false; @@ -3211,7 +3215,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); r1.setIcon("icon_"+m+"extension_simple.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); - generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc); + generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc, new ArrayList()); } } } else { @@ -3324,7 +3328,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (isProfiledExtension(ec)) { StructureDefinition extDefn = context.getContext().fetchResource(StructureDefinition.class, ec.getType().get(0).getProfile().get(0).getValue()); if (extDefn == null) { - generateElementInner(t, sd, ec, 1, null, compareElement, null, false); + generateElementInner(t, sd, ec, 1, null, compareElement, null, false, "", anchorPrefix, elements); } else { ElementDefinition valueDefn = getExtensionValueDefinition(extDefn); ElementDefinition compareValueDefn = null; @@ -3332,15 +3336,15 @@ public class StructureDefinitionRenderer extends ResourceRenderer { StructureDefinition compareExtDefn = context.getContext().fetchResource(StructureDefinition.class, compareElement.getType().get(0).getProfile().get(0).getValue()); compareValueDefn = getExtensionValueDefinition(extDefn); } catch (Exception except) {} - generateElementInner(t, sd, ec, valueDefn == null || valueDefn.prohibited() ? 2 : 3, valueDefn, compareElement, compareValueDefn, false); + generateElementInner(t, sd, ec, valueDefn == null || valueDefn.prohibited() ? 2 : 3, valueDefn, compareElement, compareValueDefn, false, "", anchorPrefix, elements); // generateElementInner(b, extDefn, extDefn.getSnapshot().getElement().get(0), valueDefn == null ? 2 : 3, valueDefn); } } else { while (!dstack.isEmpty() && !isParent(dstack.peek(), ec)) { - finish(t, sd, dstack.pop(), mode); + finish(t, sd, dstack.pop(), mode, "", anchorPrefix); } dstack.push(ec); - generateElementInner(t, sd, ec, mode, null, compareElement, null, false); + generateElementInner(t, sd, ec, mode, null, compareElement, null, false, "", anchorPrefix, elements); if (ec.hasSlicing()) { generateSlicing(t, sd, ec, ec.getSlicing(), compareElement, mode, false); } @@ -3350,12 +3354,12 @@ public class StructureDefinitionRenderer extends ResourceRenderer { i++; } while (!dstack.isEmpty()) { - finish(t, sd, dstack.pop(), mode); + finish(t, sd, dstack.pop(), mode, "", anchorPrefix); } - finish(t, sd, null, mode); + finish(t, sd, null, mode, "", anchorPrefix); } - private void finish(XhtmlNode t, StructureDefinition sd, ElementDefinition ed, int mode) throws FHIRException, IOException { + private void finish(XhtmlNode t, StructureDefinition sd, ElementDefinition ed, int mode, String defPath, String anchorPrefix) throws FHIRException, IOException { for (Base b : VersionComparisonAnnotation.getDeleted(ed == null ? sd : ed, "element")) { ElementDefinition ec = (ElementDefinition) b; @@ -3365,7 +3369,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { sp.span("color: grey", null).tx("--"); sp.b().tx(". "+title); - generateElementInner(t, sd, ec, mode, null, null, null, true); + generateElementInner(t, sd, ec, mode, null, null, null, true, defPath, anchorPrefix, new ArrayList()); if (ec.hasSlicing()) { generateSlicing(t, sd, ec, ec.getSlicing(), null, mode, true); } @@ -3685,7 +3689,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return "color:DarkGray;text-decoration:line-through"; } - private void generateElementInner(XhtmlNode tbl, StructureDefinition sd, ElementDefinition d, int mode, ElementDefinition value, ElementDefinition compare, ElementDefinition compareValue, boolean strikethrough) throws FHIRException, IOException { + private void generateElementInner(XhtmlNode tbl, StructureDefinition sd, ElementDefinition d, int mode, ElementDefinition value, ElementDefinition compare, ElementDefinition compareValue, boolean strikethrough, String defPath, String anchorPrefix, List inScopeElements) throws FHIRException, IOException { boolean root = !d.getPath().contains("."); boolean slicedExtension = d.hasSliceName() && (d.getPath().endsWith(".extension") || d.getPath().endsWith(".modifierExtension")); // int slicedExtensionMode = (mode == GEN_MODE_KEY) && slicedExtension ? GEN_MODE_SNAP : mode; // see ProfileUtilities.checkExtensionDoco / Task 3970 @@ -3779,7 +3783,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_COMP_PROF), "http://hl7.org/fhir/extensions/StructureDefinition-structuredefinition-compliesWithProfile.html", strikethrough, renderCanonicalListExt(context.formatPhrase(RenderingContext.STRUC_DEF_PROF_COMP)+" ", sd.getExtensionsByUrl(ToolingExtensions.EXT_SD_COMPLIES_WITH_PROFILE))); } - tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_OBLIG), null, strikethrough, describeObligations(d, root, sd)); + tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_OBLIG), null, strikethrough, describeObligations(d, root, sd, defPath, anchorPrefix, inScopeElements)); if (d.hasExtension(ToolingExtensions.EXT_EXTENSION_STYLE)) { String es = d.getExtensionString(ToolingExtensions.EXT_EXTENSION_STYLE); @@ -3981,7 +3985,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } - private XhtmlNode describeObligations(ElementDefinition d, boolean root, StructureDefinition sdx) throws IOException { + private XhtmlNode describeObligations(ElementDefinition d, boolean root, StructureDefinition sdx, String defPath, String anchorPrefix, List inScopeElements) throws IOException { XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); ObligationsRenderer obr = new ObligationsRenderer(corePath, sdx, d.getPath(), context, hostMd, this); obr.seeObligations(d.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)); @@ -4011,7 +4015,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } if (obr.hasObligations()) { XhtmlNode tbl = ret.table("grid"); - obr.renderTable(tbl.getChildNodes(), true); + obr.renderTable(tbl.getChildNodes(), true, defPath, anchorPrefix, inScopeElements); if (tbl.isEmpty()) { ret.remove(tbl); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java index 5f060ee7d..ff28f6333 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java @@ -439,33 +439,37 @@ public class TerminologyClientManager { } public SourcedValueSet findValueSetOnServer(String canonical) { - if (IGNORE_TX_REGISTRY || getMasterClient() == null || !useEcosystem) { + if (IGNORE_TX_REGISTRY || getMasterClient() == null) { return null; } String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&valueSet="+Utilities.URLEncode(canonical)); - if (usage != null) { - request = request + "&usage="+usage; - } String server = null; try { - JsonObject json = JsonParser.parseObjectFromUrl(request); - for (JsonObject item : json.getJsonObjects("authoritative")) { - if (server == null) { - server = item.asString("url"); + if (!useEcosystem) { + server = getMasterClient().getAddress(); + } else { + if (usage != null) { + request = request + "&usage="+usage; } - } - for (JsonObject item : json.getJsonObjects("candidates")) { - if (server == null) { - server = item.asString("url"); + JsonObject json = JsonParser.parseObjectFromUrl(request); + for (JsonObject item : json.getJsonObjects("authoritative")) { + if (server == null) { + server = item.asString("url"); + } } - } - if (server == null) { - return null; - } - if (server.contains("://tx.fhir.org")) { - try { - server = server.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost()); - } catch (MalformedURLException e) { + for (JsonObject item : json.getJsonObjects("candidates")) { + if (server == null) { + server = item.asString("url"); + } + } + if (server == null) { + return null; + } + if (server.contains("://tx.fhir.org")) { + try { + server = server.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost()); + } catch (MalformedURLException e) { + } } } TerminologyClientContext client = serverMap.get(server); diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index 516a99a1b..4c1a950b0 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 43f9ae2c5..e45820a9a 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java index 387240cdd..8ec6b1dd7 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java @@ -958,10 +958,12 @@ public class NpmPackage { public String fhirVersion() { if ("hl7.fhir.core".equals(npm.asString("name"))) return npm.asString("version"); - else if (npm.asString("name").startsWith("hl7.fhir.r2.") || npm.asString("name").startsWith("hl7.fhir.r2b.") || npm.asString("name").startsWith("hl7.fhir.r3.") || - npm.asString("name").startsWith("hl7.fhir.r4.") || npm.asString("name").startsWith("hl7.fhir.r4b.") || npm.asString("name").startsWith("hl7.fhir.r5.")) + else if ( + Utilities.existsInList(npm.asString("type"), "fhir.core", "fhir.examples") && + Utilities.startsWithInList( npm.asString("name"), "hl7.fhir.r2.", "hl7.fhir.r2b.", "hl7.fhir.r3.", + "hl7.fhir.r4.", "hl7.fhir.r4b.", "hl7.fhir.r5.")) { return npm.asString("version"); - else { + } else { JsonObject dep = null; if (npm.hasObject("dependencies")) { dep = npm.getJsonObject("dependencies"); 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 492247773..39298308d 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 @@ -120,7 +120,7 @@ public class HierarchicalTableGenerator { public static final int CONTINUE_SLICE = 5; private static final String BACKGROUND_ALT_COLOR = "#F7F7F7"; public static boolean ACTIVE_TABLES = false; - + public enum TextAlignment { LEFT, CENTER, RIGHT; } @@ -617,7 +617,9 @@ public class HierarchicalTableGenerator { private String dest; private boolean makeTargets; - + private String defPath = ""; + private String anchorPrefix = ""; + /** * There are circumstances where the table has to present in the absence of a stable supporting infrastructure. * and the file paths cannot be guaranteed. For these reasons, you can tell the builder to inline all the graphics @@ -628,7 +630,7 @@ public class HierarchicalTableGenerator { private TableGenerationMode mode; private RenderingI18nContext i18n; - + public HierarchicalTableGenerator(RenderingI18nContext i18n) { super(); this.i18n = i18n; @@ -643,6 +645,14 @@ public class HierarchicalTableGenerator { checkSetup(); } + public String getDefPath() { + return defPath; + } + + public String getAnchorPrefix() { + return anchorPrefix; + } + private void checkSetup() { if (dest == null) { throw new Error("what"); @@ -650,6 +660,17 @@ public class HierarchicalTableGenerator { } + public HierarchicalTableGenerator(RenderingI18nContext i18n, String dest, boolean inlineGraphics, boolean makeTargets, String defPath, String anchorPrefix) { + super(); + this.i18n = i18n; + this.dest = dest; + this.inLineGraphics = inlineGraphics; + this.makeTargets = makeTargets; + this.defPath = defPath; + this.anchorPrefix = anchorPrefix; + checkSetup(); + } + public HierarchicalTableGenerator(RenderingI18nContext i18n, String dest, boolean inlineGraphics, boolean makeTargets) { super(); this.i18n = i18n; diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 2de486d04..08d4d11b7 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index 9af85004b..38533a588 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 2752c307a..77a27419b 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -887,6 +887,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP } validator.setJurisdiction(jurisdiction); validator.setLogProgress(true); + if (policyAdvisor != null) { + validator.setPolicyAdvisor(policyAdvisor); + } return validator; } @@ -1259,7 +1262,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, IMessagingServices msgServices, List messages) { - return new BasePolicyAdvisorForFullValidation().getImpliedProfilesForResource(validator, appContext, stackPath, + return new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID).getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index 6d93f182f..14ce091f4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -58,7 +58,7 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati private Map pidMap = new HashMap<>(); public StandAloneValidatorFetcher(FilesystemPackageCacheManager pcm, IWorkerContext context, IPackageInstaller installer) { - super(); + super(ReferenceValidationPolicy.IGNORE); this.pcm = pcm; this.context = context; this.installer = installer; @@ -74,7 +74,7 @@ public class StandAloneValidatorFetcher extends BasePolicyAdvisorForFullValidati Object appContext, String path, String url) { - return ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS; + return ReferenceValidationPolicy.IGNORE; } @Override diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index 9d100cd94..49eae3937 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -568,6 +568,7 @@ public class ValidationService { StandAloneValidatorFetcher fetcher = new StandAloneValidatorFetcher(validationEngine.getPcm(), validationEngine.getContext(), validationEngine); validationEngine.setFetcher(fetcher); validationEngine.getContext().setLocator(fetcher); + validationEngine.setPolicyAdvisor(fetcher); } validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules()); validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction())); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java index 0b012b23e..d3046ab93 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/BasePolicyAdvisorForFullValidation.java @@ -27,10 +27,17 @@ import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; public class BasePolicyAdvisorForFullValidation implements IValidationPolicyAdvisor { + + private ReferenceValidationPolicy refpol = ReferenceValidationPolicy.CHECK_VALID; + + public BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy refpol) { + super(); + this.refpol = refpol; + } @Override public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { - return ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS; + return refpol; } @Override 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 be75b0df8..5e01fb873 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 @@ -596,7 +596,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean noBindingMsgSuppressed; private Map fetchCache = new HashMap<>(); private HashMap resourceTracker = new HashMap<>(); - private IValidationPolicyAdvisor policyAdvisor = new BasePolicyAdvisorForFullValidation(); + private IValidationPolicyAdvisor policyAdvisor = new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID); long time = 0; long start = 0; long lastlog = 0; @@ -1029,7 +1029,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false, false); } } - if (hintAboutNonMustSupport) { + if (hintAboutNonMustSupport && !profiles.isEmpty()) { checkElementUsage(errors, element, stack); } codingObserver.finish(errors, stack); @@ -1042,10 +1042,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkElementUsage(List errors, Element element, NodeStack stack) { - String elementUsage = element.getUserString("elementSupported"); - hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getVersionedUrl()); + if (element.getPath()==null + || (element.getName().equals("id") && !element.getPath().substring(0, element.getPath().length()-3).contains(".")) + || (element.getName().equals("text") && !element.getPath().substring(0, element.getPath().length()-5).contains("."))) + return; + String hasFixed = element.getUserString("hasFixed"); + if (element.getPath().contains(".") && (hasFixed== null || !hasFixed.equals("Y"))) { + String elementUsage = element.getUserString("elementSupported"); + hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage != null && (elementUsage.equals("Y") || elementUsage.equals("NA")), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getVersionedUrl()); + if (elementUsage==null || !elementUsage.equals("Y")) + return; + } - if (element.hasChildren()) { + if (element.hasChildren() && (hasFixed== null || !hasFixed.equals("Y"))) { String prevName = ""; int elementCount = 0; for (Element ce : element.getChildren()) { @@ -6745,13 +6754,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } profile.setUserData("usesMustSupport", usesMustSupport); } - if (usesMustSupport.equals("Y")) { - String elementSupported = ei.getElement().getUserString("elementSupported"); - if (elementSupported == null || ei.definition.getMustSupport()) - if (ei.definition.getMustSupport()) { - ei.getElement().setUserData("elementSupported", "Y"); - } - } + String elementSupported = ei.getElement().getUserString("elementSupported"); + String fixedValue = ei.getElement().getUserString("hasFixed"); + if ((elementSupported == null || !elementSupported.equals("Y")) && ei.definition.getMustSupport()) { + if (ei.definition.getMustSupport()) { + ei.getElement().setUserData("elementSupported", "Y"); + } + } else if (elementSupported == null && !usesMustSupport.equals("Y")) + ei.getElement().setUserData("elementSupported", "NA"); + if (fixedValue==null && (ei.definition.hasFixed() || ei.definition.hasPattern())) + ei.getElement().setUserData("hasFixed", "Y"); } public boolean checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java index 32316e4f9..a02dd685d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java @@ -1,5 +1,5 @@ -package org.hl7.fhir.validation.profile; - +package org.hl7.fhir.validation.profile; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -28,138 +28,140 @@ package org.hl7.fhir.validation.profile; POSSIBILITY OF SUCH DAMAGE. */ - - -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; - -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; -import org.hl7.fhir.r5.model.ElementDefinition; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; -import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; -import org.hl7.fhir.r5.utils.XVerExtensionManager; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.validation.ValidationMessage; -import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.hl7.fhir.validation.BaseValidator; - -public class ProfileValidator extends BaseValidator { - - private boolean checkAggregation = false; - private boolean checkMustSupport = false; - private boolean allowDoubleQuotesInFHIRPath = false; - private FHIRPathEngine fpe; - - public ProfileValidator(IWorkerContext context, XVerExtensionManager xverManager) { - super(context, xverManager, false); - fpe = new FHIRPathEngine(context); - fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath); - } - - public boolean isCheckAggregation() { - return checkAggregation; - } - - public boolean isCheckMustSupport() { - return checkMustSupport; - } - - public void setCheckAggregation(boolean checkAggregation) { - this.checkAggregation = checkAggregation; - } - - public void setCheckMustSupport(boolean checkMustSupport) { - this.checkMustSupport = checkMustSupport; - } - - public boolean isAllowDoubleQuotesInFHIRPath() { - return allowDoubleQuotesInFHIRPath; - } - - public void setAllowDoubleQuotesInFHIRPath(boolean allowDoubleQuotesInFHIRPath) { - this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath; - } - - protected boolean rule(List errors, IssueType type, String path, boolean b, String msg) { - String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path; - return super.ruleHtml(errors, NO_RULE_DATE, type, path, b, msg, ""+rn+": "+Utilities.escapeXml(msg)); - } - - public List validate(StructureDefinition profile, boolean forBuild) { - List errors = new ArrayList(); - - // must have a FHIR version- GF#3160 - String s = (profile.getKind() == StructureDefinitionKind.LOGICAL) ? "Logical Models" : "Profiles"; - warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasFhirVersion(), s+" SHOULD state the FHIR Version on which they are based"); - warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasVersion(), s+" SHOULD state their own version"); - - // extensions must be defined - for (ElementDefinition ec : profile.getDifferential().getElement()) - checkExtensions(profile, errors, "differential", ec); - rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "missing Snapshot at "+profile.getName()+"."+profile.getName()); - for (ElementDefinition ec : profile.getSnapshot().getElement()) - checkExtensions(profile, errors, "snapshot", ec); - - if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "A snapshot is required")) { - Hashtable snapshotElements = new Hashtable(); - for (ElementDefinition ed : profile.getSnapshot().getElement()) { - snapshotElements.put(ed.getId(), ed); - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (forBuild) { - if (!inExemptList(inv.getKey())) { -// if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId()+"::"+ed.getPath()+"::"+inv.getKey(), inv.hasExpression(), "The invariant has no FHIR Path expression ("+inv.getXpath()+")")) { -// try { -// fpe.check(null, profile.getType(), ed.getPath(), inv.getExpression()); // , inv.hasXpath() && inv.getXpath().startsWith("@value") -// } catch (Exception e) { -// // rule(errors, UNKNOWN_DATE_TIME, IssueType.STRUCTURE, profile.getId()+"::"+ed.getPath()+"::"+inv.getId(), false, e.getMessage()); -// } -// } - } - } - } - } - if (snapshotElements != null) { - for (ElementDefinition diffElement : profile.getDifferential().getElement()) { - if (diffElement == null) - throw new Error("Diff Element is null - this is not an expected thing"); - ElementDefinition snapElement = snapshotElements.get(diffElement.getId()); - if (snapElement!=null) { // Happens with profiles in the main build - should be able to fix once snapshot generation is fixed - Lloyd - warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), !checkMustSupport || snapElement.hasMustSupport(), "Elements included in the differential should declare mustSupport"); - if (checkAggregation) { - for (TypeRefComponent type : snapElement.getType()) { - if ("http://hl7.org/fhir/Reference".equals(type.getWorkingCode()) || "http://hl7.org/fhir/canonical".equals(type.getWorkingCode())) { - warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), type.hasAggregation(), "Elements with type Reference or canonical should declare aggregation"); - } - } - } - } - } - } - } - return errors; - } - - // these are special cases - private boolean inExemptList(String key) { - return key.startsWith("txt-"); - } - - private boolean checkExtensions(StructureDefinition profile, List errors, String kind, ElementDefinition ec) { - if (!ec.getType().isEmpty() && "Extension".equals(ec.getType().get(0).getWorkingCode()) && ec.getType().get(0).hasProfile()) { - String url = ec.getType().get(0).getProfile().get(0).getValue(); - StructureDefinition defn = context.fetchResource(StructureDefinition.class, url); - if (defn == null) { - defn = getXverExt(profile, errors, url); - } - return rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId(), defn != null, "Unable to find Extension '"+url+"' referenced at "+profile.getUrl()+" "+kind+" "+ec.getPath()+" ("+ec.getSliceName()+")"); - } else { - return true; - } - } - - + + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; +import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; +import org.hl7.fhir.validation.BaseValidator; + +public class ProfileValidator extends BaseValidator { + + private boolean checkAggregation = false; + private boolean checkMustSupport = false; + private boolean allowDoubleQuotesInFHIRPath = false; + private FHIRPathEngine fpe; + + public ProfileValidator(IWorkerContext context, XVerExtensionManager xverManager) { + super(context, xverManager, false); + fpe = new FHIRPathEngine(context); + fpe.setAllowDoubleQuotes(allowDoubleQuotesInFHIRPath); + } + + public boolean isCheckAggregation() { + return checkAggregation; + } + + public boolean isCheckMustSupport() { + return checkMustSupport; + } + + public void setCheckAggregation(boolean checkAggregation) { + this.checkAggregation = checkAggregation; + } + + public void setCheckMustSupport(boolean checkMustSupport) { + this.checkMustSupport = checkMustSupport; + } + + public boolean isAllowDoubleQuotesInFHIRPath() { + return allowDoubleQuotesInFHIRPath; + } + + public void setAllowDoubleQuotesInFHIRPath(boolean allowDoubleQuotesInFHIRPath) { + this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath; + } + + protected boolean rule(List errors, IssueType type, String path, boolean b, String msg) { + String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path; + return super.ruleHtml(errors, NO_RULE_DATE, type, path, b, msg, ""+rn+": "+Utilities.escapeXml(msg)); + } + + public List validate(StructureDefinition profile, boolean forBuild) { + List errors = new ArrayList(); + + // must have a FHIR version- GF#3160 + String s = (profile.getKind() == StructureDefinitionKind.LOGICAL) ? "Logical Models" : "Profiles"; + warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasFhirVersion(), s+" SHOULD state the FHIR Version on which they are based"); + warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getUrl(), profile.hasVersion(), s+" SHOULD state their own version"); + + // extensions must be defined + for (ElementDefinition ec : profile.getDifferential().getElement()) + checkExtensions(profile, errors, "differential", ec); + rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "missing Snapshot at "+profile.getName()+"."+profile.getName()); + for (ElementDefinition ec : profile.getSnapshot().getElement()) + checkExtensions(profile, errors, "snapshot", ec); + + if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "A snapshot is required")) { + Hashtable snapshotElements = new Hashtable(); + for (ElementDefinition ed : profile.getSnapshot().getElement()) { + snapshotElements.put(ed.getId(), ed); + for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { + if (forBuild) { + if (!inExemptList(inv.getKey())) { +// if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId()+"::"+ed.getPath()+"::"+inv.getKey(), inv.hasExpression(), "The invariant has no FHIR Path expression ("+inv.getXpath()+")")) { +// try { +// fpe.check(null, profile.getType(), ed.getPath(), inv.getExpression()); // , inv.hasXpath() && inv.getXpath().startsWith("@value") +// } catch (Exception e) { +// // rule(errors, UNKNOWN_DATE_TIME, IssueType.STRUCTURE, profile.getId()+"::"+ed.getPath()+"::"+inv.getId(), false, e.getMessage()); +// } +// } + } + } + } + } + if (snapshotElements != null) { + for (ElementDefinition diffElement : profile.getDifferential().getElement()) { + if (diffElement == null) + throw new Error("Diff Element is null - this is not an expected thing"); + ElementDefinition snapElement = snapshotElements.get(diffElement.getId()); + if (snapElement!=null) { // Happens with profiles in the main build - should be able to fix once snapshot generation is fixed - Lloyd + // We don't care about mustSupport for the root element, if the element is just declaring slicing, if there's a pattern or fixed value, or if the element is being prohibited. + boolean noMustSupport = !checkMustSupport || !snapElement.getPath().contains(".") || snapElement.hasSlicing() || snapElement.hasPattern() || snapElement.hasFixed() || snapElement.getMax().equals("0") || snapElement.hasMustSupport(); + warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), noMustSupport, "Elements included in the differential that aren't prohibited and don't have fixed values or patterns should declare mustSupport: " + snapElement.getPath()); + if (checkAggregation) { + for (TypeRefComponent type : snapElement.getType()) { + if ("http://hl7.org/fhir/Reference".equals(type.getWorkingCode()) || "http://hl7.org/fhir/canonical".equals(type.getWorkingCode())) { + warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, diffElement.getId(), type.hasAggregation(), "Elements with type Reference or canonical should declare aggregation"); + } + } + } + } + } + } + } + return errors; + } + + // these are special cases + private boolean inExemptList(String key) { + return key.startsWith("txt-"); + } + + private boolean checkExtensions(StructureDefinition profile, List errors, String kind, ElementDefinition ec) { + if (!ec.getType().isEmpty() && "Extension".equals(ec.getType().get(0).getWorkingCode()) && ec.getType().get(0).hasProfile()) { + String url = ec.getType().get(0).getProfile().get(0).getValue(); + StructureDefinition defn = context.fetchResource(StructureDefinition.class, url); + if (defn == null) { + defn = getXverExt(profile, errors, url); + } + return rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, profile.getId(), defn != null, "Unable to find Extension '"+url+"' referenced at "+profile.getUrl()+" "+kind+" "+ec.getPath()+" ("+ec.getSliceName()+")"); + } else { + return true; + } + } + + } \ No newline at end of file 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 f7bfaaff0..11cd9d6cd 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 @@ -894,7 +894,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, Element resource, boolean valid, IMessagingServices msgServices, List messages) { - return new BasePolicyAdvisorForFullValidation().getImpliedProfilesForResource(validator, appContext, stackPath, + return new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.CHECK_VALID).getImpliedProfilesForResource(validator, appContext, stackPath, definition, structure, resource, valid, msgServices, messages); } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java index 31f8e3e6a..f8a5be0ac 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/utilities/TestUtilities.java @@ -5,10 +5,13 @@ import java.util.Locale; import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache; import org.hl7.fhir.r5.test.utils.TestingUtilities; +import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.tests.TestConfig; import org.hl7.fhir.utilities.tests.TestConstants; import org.hl7.fhir.validation.ValidationEngine; +import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher; +import org.hl7.fhir.validation.instance.BasePolicyAdvisorForFullValidation; public class TestUtilities { @@ -64,6 +67,7 @@ public class TestUtilities { TerminologyCache.setCacheErrors(true); } + validationEngine.setPolicyAdvisor(new BasePolicyAdvisorForFullValidation(ReferenceValidationPolicy.IGNORE)); return validationEngine; } 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 e532c5c4e..c09095f3e 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,5 +1,6 @@ { "http://hl7.org/fhir/ValueSet/ucum-vitals-common|4.0.0" : null, + "http://loinc.org/vs" : null, "http://hl7.org.au/fhir/ValueSet/au-hl7v2-0203" : null, "http://hl7.org/fhir/ValueSet/name-use|4.0.1" : null, "http://hl7.org/fhir/ValueSet/observation-status|4.0.0" : null, diff --git a/pom.xml b/pom.xml index 05f3bbd3c..e79cd85a3 100644 --- a/pom.xml +++ b/pom.xml @@ -14,14 +14,14 @@ HAPI FHIR --> org.hl7.fhir.core - 6.3.11-SNAPSHOT + 6.3.12-SNAPSHOT pom 1.26.0 32.0.1-jre 6.4.1 - 1.5.11-SNAPSHOT + 1.5.11 2.17.0 5.9.2 1.8.2