diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..8b14c36a0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1,8 @@ +Validator: +* Fix bug in unknown URL handling that could cause significant delays in validation (>1min / unknown URL) +* fix bug in processing excludes with filters in value sets + +Other code changes: +* fix case problem with icons in questionnaire rendering +* fix problem with rendering nested slices on content references +* fix rendering on must-support view for must-support on types and target profiles \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index cc58a3275..f73020743 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -150,6 +150,26 @@ import org.hl7.fhir.utilities.xml.SchematronWriter.Section; */ public class ProfileUtilities extends TranslatingUtilities { + public class ElementDefinitionResolution { + + private StructureDefinition source; + private ElementDefinition element; + + public ElementDefinitionResolution(StructureDefinition source, ElementDefinition element) { + this.source = source; + this.element = element; + } + + public StructureDefinition getSource() { + return source; + } + + public ElementDefinition getElement() { + return element; + } + + } + public class ElementRedirection { private String path; @@ -1154,15 +1174,24 @@ public class ProfileUtilities extends TranslatingUtilities { while (diffCursor <= diffLimit && differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+".")) diffCursor++; if (outcome.hasContentReference()) { - ElementDefinition tgt = getElementById(srcSD, base.getElement(), outcome.getContentReference()); + ElementDefinitionResolution tgt = getElementById(srcSD, base.getElement(), outcome.getContentReference()); if (tgt == null) throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_REFERENCE_TO_, outcome.getContentReference())); - replaceFromContentReference(outcome, tgt); - int nbc = base.getElement().indexOf(tgt)+1; - int nbl = nbc; - while (nbl < base.getElement().size() && base.getElement().get(nbl).getPath().startsWith(tgt.getPath()+".")) - nbl++; - processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); + replaceFromContentReference(outcome, tgt.getElement()); + if (tgt.getSource() != srcSD) { + base = tgt.getSource().getSnapshot(); + int nbc = base.getElement().indexOf(tgt.getElement())+1; + int nbl = nbc; + while (nbl < base.getElement().size() && base.getElement().get(nbl).getPath().startsWith(tgt.getElement().getPath()+".")) + nbl++; + processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getElement().getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), tgt.getSource()); + } else { + int nbc = base.getElement().indexOf(tgt.getElement())+1; + int nbl = nbc; + while (nbl < base.getElement().size() && base.getElement().get(nbl).getPath().startsWith(tgt.getElement().getPath()+".")) + nbl++; + processPaths(indent+" ", result, base, differential, nbc, start - 1, nbl-1, diffCursor - 1, url, webUrl, profileName, tgt.getElement().getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, null, null, redirectorStack(redirector, outcome, cpath), srcSD); + } } else { StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element"); if (dt == null) @@ -1232,25 +1261,25 @@ public class ProfileUtilities extends TranslatingUtilities { } // check the slice names too while we're at it... for (TypeSlice ts : typeList) { - if (ts.type != null) { - String tn = rootName(cpath)+Utilities.capitalize(ts.type); - if (!ts.defn.hasSliceName()) { - ts.defn.setSliceName(tn); - } else if (!ts.defn.getSliceName().equals(tn)) { - if (autoFixSliceNames) { + if (ts.type != null) { + String tn = rootName(cpath)+Utilities.capitalize(ts.type); + if (!ts.defn.hasSliceName()) { ts.defn.setSliceName(tn); - } else { - throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_NAME_MUST_BE__BUT_IS_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.getSliceName())); + } else if (!ts.defn.getSliceName().equals(tn)) { + if (autoFixSliceNames) { + ts.defn.setSliceName(tn); + } else { + throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_NAME_MUST_BE__BUT_IS_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.getSliceName())); + } + } if (!ts.defn.hasType()) { + ts.defn.addType().setCode(ts.type); + } else if (ts.defn.getType().size() > 1) { + throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_MORE_THAN_ONE_TYPE_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.typeSummary())); + } else if (!ts.defn.getType().get(0).getCode().equals(ts.type)) { + throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_WRONG_TYPE_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.typeSummary())); } - } if (!ts.defn.hasType()) { - ts.defn.addType().setCode(ts.type); - } else if (ts.defn.getType().size() > 1) { - throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_MORE_THAN_ONE_TYPE_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.typeSummary())); - } else if (!ts.defn.getType().get(0).getCode().equals(ts.type)) { - throw new FHIRException(context.formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_WRONG_TYPE_, (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath), tn, ts.defn.typeSummary())); } } - } // ok passed the checks. // copy the root diff, and then process any children it has @@ -1263,17 +1292,7 @@ public class ProfileUtilities extends TranslatingUtilities { e.getSlicing().addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); e.getSlicing().setRules(SlicingRules.CLOSED); // type slicing is always closed; the differential might call it open, but that just means it's not constraining the slices it doesn't mention e.getSlicing().setOrdered(false); - // check that there's a slice for each allowed types - Set allowedTypes = getListOfTypes(e); - for (TypeSlice t : typeList) { - allowedTypes.remove(t.type); - } - // GG - work in progress.. -// if (!allowedTypes.isEmpty()) { -// System.out.println("!!: Error at "+cpath+": Allowed Types not sliced = "+allowedTypes); -// throw new Error("Error at "+cpath+": Allowed Types not sliced = "+allowedTypes); -// } - + start++; String fixedType = null; @@ -1305,7 +1324,32 @@ public class ProfileUtilities extends TranslatingUtilities { } } } - + if (!"0".equals(e.getMax())) { + // check that there's a slice for each allowed types + Set allowedTypes = getListOfTypes(e); + for (TypeSlice t : typeList) { + if (t.type != null) { + allowedTypes.remove(t.type); + } else if (t.getDefn().hasSliceName() && t.getDefn().getType().size() == 1) { + allowedTypes.remove(t.getDefn().getType().get(0).getCode()); + } + } + if (!allowedTypes.isEmpty()) { + if (cpath.contains("xtension.value")) { + for (Iterator iter = e.getType().iterator(); iter.hasNext(); ) { + TypeRefComponent tr = iter.next(); + if (allowedTypes.contains(tr.getCode())) { + iter.remove(); + } + } +// System.out.println("!!: Extension Error at "+cpath+": Allowed Types not sliced = "+allowedTypes+". !Extension!!"); +// throw new Error("Extension Error at "+cpath+": Allowed Types not sliced = "+allowedTypes+". !Extension!!"); + + } else { + e.getSlicing().setRules(SlicingRules.OPEN); + } + } + } // ok, done with that - next in the base list baseCursor = nbl+1; diffCursor = ndl+1; @@ -1989,7 +2033,7 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean diffsConstrainTypes(List diffMatches, String cPath, List typeList) { // if (diffMatches.size() < 2) -// return false; + // return false; String p = diffMatches.get(0).getPath(); if (!p.endsWith("[x]") && !cPath.endsWith("[x]")) return false; @@ -2006,11 +2050,17 @@ public class ProfileUtilities extends TranslatingUtilities { if (ed.hasSliceName() && ed.getType().size() == 1) { typeList.add(new TypeSlice(ed, ed.getTypeFirstRep().getWorkingCode())); } else if (ed.hasSliceName() && ed.getType().size() == 0) { - String tn = ed.getSliceName().substring(rn.length()); - if (isDataType(tn)) { - typeList.add(new TypeSlice(ed, tn)); - } else if (isPrimitive(Utilities.uncapitalize(tn))) { - typeList.add(new TypeSlice(ed, Utilities.uncapitalize(tn))); + if (isDataType(s)) { + typeList.add(new TypeSlice(ed, s)); + } else if (isPrimitive(Utilities.uncapitalize(s))) { + typeList.add(new TypeSlice(ed, Utilities.uncapitalize(s))); + } else { + String tn = ed.getSliceName().substring(n.length()); + if (isDataType(tn)) { + typeList.add(new TypeSlice(ed, tn)); + } else if (isPrimitive(Utilities.uncapitalize(tn))) { + typeList.add(new TypeSlice(ed, Utilities.uncapitalize(tn))); + } } } else if (!ed.hasSliceName() && !s.equals("[x]")) { if (isDataType(s)) @@ -2020,9 +2070,9 @@ public class ProfileUtilities extends TranslatingUtilities { else if (isPrimitive(Utilities.uncapitalize(s))) typeList.add(new TypeSlice(ed, Utilities.uncapitalize(s))); } else if (!ed.hasSliceName() && s.equals("[x]")) - typeList.add(new TypeSlice(ed, null)); - } - } + typeList.add(new TypeSlice(ed, null)); + } + } return true; } @@ -3171,7 +3221,7 @@ public class ProfileUtilities extends TranslatingUtilities { r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), ((UriType) ued.getFixed()).getValue(), null, null)); r1.getCells().add(gen.new Cell()); r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); - genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false); + genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); Cell cell = gen.new Cell(); cell.addMarkdown(c.getDefinition()); r1.getCells().add(cell); @@ -3184,7 +3234,7 @@ public class ProfileUtilities extends TranslatingUtilities { ved = ted; } - genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false); + genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false); r.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); } @@ -3243,7 +3293,7 @@ public class ProfileUtilities extends TranslatingUtilities { private static final int AGG_GR = 2; private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; - private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root) { + private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean mustSupportMode) { Cell c = gen.new Cell(); r.getCells().add(c); if (e.hasContentReference()) { @@ -3294,88 +3344,94 @@ public class ProfileUtilities extends TranslatingUtilities { TypeRefComponent tl = null; for (TypeRefComponent t : types) { - if (first) { - first = false; - } else { - c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); - } - tl = t; - if (t.hasTarget()) { - c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); - if (isMustSupportDirect(t) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + if (!mustSupportMode || allTypesMustSupport(e) || isMustSupport(t)) { + if (first) { + first = false; + } else { + c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); } - c.getPieces().add(gen.new Piece(null, "(", null)); - boolean tfirst = true; - for (CanonicalType u : t.getTargetProfile()) { - if (tfirst) - tfirst = false; - else - c.addPiece(gen.new Piece(null, " | ", null)); - genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue()); - if (isMustSupport(u) && e.getMustSupport()) { + tl = t; + if (t.hasTarget()) { + c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); + if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); } - } - c.getPieces().add(gen.new Piece(null, ")", null)); - if (t.getAggregation().size() > 0) { - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); - boolean firstA = true; - for (Enumeration a : t.getAggregation()) { - if (firstA = true) - firstA = false; - else - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); - } - c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); - } - } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type - String ref; - boolean pfirst = true; - for (CanonicalType p : t.getProfile()) { - if (pfirst) { - pfirst = false; - } else { - c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); - } - - ref = pkp.getLinkForProfile(profile, p.getValue()); - if (ref != null) { - String[] parts = ref.split("\\|"); - if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { - // c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], "<" + parts[1] + ">", t.getCode()))); Lloyd - c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); - } else { - // c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().startsWith(corePath)? corePath: "")+parts[0], "<" + parts[1] + ">", t.getCode()))); - c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); + c.getPieces().add(gen.new Piece(null, "(", null)); + boolean tfirst = true; + for (CanonicalType u : t.getTargetProfile()) { + if (!mustSupportMode || allProfilesMustSupport(t.getTargetProfile()) || isMustSupport(u)) { + if (tfirst) + tfirst = false; + else + c.addPiece(gen.new Piece(null, " | ", null)); + genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue()); + if (!mustSupportMode && isMustSupport(u) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } } - } else - c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); - if (isMustSupport(p) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); } - } - } else { - String tc = t.getWorkingCode(); - if (Utilities.isAbsoluteUrl(tc)) { - StructureDefinition sd = context.fetchTypeDefinition(tc); - if (sd == null) { + c.getPieces().add(gen.new Piece(null, ")", null)); + if (t.getAggregation().size() > 0) { + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); + boolean firstA = true; + for (Enumeration a : t.getAggregation()) { + if (firstA = true) + firstA = false; + else + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); + } + c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); + } + } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type + String ref; + boolean pfirst = true; + for (CanonicalType p : t.getProfile()) { + if (!mustSupportMode || allProfilesMustSupport(t.getProfile()) || isMustSupport(p)) { + if (pfirst) { + pfirst = false; + } else { + c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); + } + + ref = pkp.getLinkForProfile(profile, p.getValue()); + if (ref != null) { + String[] parts = ref.split("\\|"); + if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { + // c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], "<" + parts[1] + ">", t.getCode()))); Lloyd + c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); + } else { + // c.addPiece(checkForNoChange(t, gen.new Piece((t.getProfile().startsWith(corePath)? corePath: "")+parts[0], "<" + parts[1] + ">", t.getCode()))); + c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); + } + } else + c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); + if (!mustSupportMode && isMustSupport(p) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } + } + } + } else { + String tc = t.getWorkingCode(); + if (Utilities.isAbsoluteUrl(tc)) { + StructureDefinition sd = context.fetchTypeDefinition(tc); + if (sd == null) { + c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); + } else { + c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), sd.getType(), null))); + } + } else if (pkp != null && pkp.hasLinkFor(tc)) { c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); } else { - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), sd.getType(), null))); + c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); + } + if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); } - } else if (pkp != null && pkp.hasLinkFor(tc)) { - c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); - } else { - c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); - } - if (isMustSupportDirect(t) && e.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); } } } @@ -3485,7 +3541,7 @@ public class ProfileUtilities extends TranslatingUtilities { // return null; } - private ElementDefinition getElementById(StructureDefinition source, List elements, String contentReference) { + private ElementDefinitionResolution getElementById(StructureDefinition source, List elements, String contentReference) { if (!contentReference.startsWith("#") && contentReference.contains("#")) { String url = contentReference.substring(0, contentReference.indexOf("#")); contentReference = contentReference.substring(contentReference.indexOf("#")); @@ -3499,7 +3555,7 @@ public class ProfileUtilities extends TranslatingUtilities { } for (ElementDefinition ed : elements) if (ed.hasId() && ("#"+ed.getId()).equals(contentReference)) - return ed; + return new ElementDefinitionResolution(source, ed); return null; } @@ -3882,7 +3938,7 @@ public class ProfileUtilities extends TranslatingUtilities { // genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); } if (typesRow != null) { - makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName); + makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport); } } return slicingRow; @@ -3967,7 +4023,7 @@ public class ProfileUtilities extends TranslatingUtilities { res.add(genCardinality(gen, element, row, hasDef, used, extDefn.getElement())); ElementDefinition valueDefn = extDefn.getExtensionValueDefinition(); if (valueDefn != null && !"0".equals(valueDefn.getMax())) - res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root)); + res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); else // if it's complex, we just call it nothing // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null))); @@ -3978,7 +4034,7 @@ public class ProfileUtilities extends TranslatingUtilities { if ("0".equals(element.getMax())) res.add(addCell(row, gen.new Cell())); else - res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root)); + res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); } } else { @@ -3986,7 +4042,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (element.hasSlicing()) res.add(addCell(row, gen.new Cell(null, corePath+"profiling.html#slicing", "(Slice Definition)", null, null))); else if (hasDef && !"0".equals(element.getMax()) && typesRow == null) - res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root)); + res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); else res.add(addCell(row, gen.new Cell())); res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); @@ -4048,96 +4104,101 @@ public class ProfileUtilities extends TranslatingUtilities { return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); } - private void makeChoiceRows(List subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName) { + private void makeChoiceRows(List subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode) { // create a child for each choice for (TypeRefComponent tr : element.getType()) { - Row choicerow = gen.new Row(); - String t = tr.getWorkingCode(); - if (isReference(t)) { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); - choicerow.getCells().add(gen.new Cell()); - choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - Cell c = gen.new Cell(); - choicerow.getCells().add(c); - if (ADD_REFERENCE_TO_TABLE) { - if (tr.getWorkingCode().equals("canonical")) - c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); - else - c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); - if (isMustSupportDirect(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - c.getPieces().add(gen.new Piece(null, "(", null)); - } - boolean first = true; - for (CanonicalType rt : tr.getTargetProfile()) { - if (!first) - c.getPieces().add(gen.new Piece(null, " | ", null)); - genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue()); - if (isMustSupport(rt) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); - } - first = false; - } - if (first) - c.getPieces().add(gen.new Piece(null, "Any", null)); - - if (ADD_REFERENCE_TO_TABLE) - c.getPieces().add(gen.new Piece(null, ")", null)); - - } else { - StructureDefinition sd = context.fetchTypeDefinition(t); - if (sd == null) { - System.out.println("Unable to find "+t); - sd = context.fetchTypeDefinition(t); - } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); + if (!mustSupportMode || allTypesMustSupport(element) || isMustSupport(tr)) { + Row choicerow = gen.new Row(); + String t = tr.getWorkingCode(); + if (isReference(t)) { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null); + choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + Cell c = gen.new Cell(); choicerow.getCells().add(c); - if (isMustSupport(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - } else { - choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); - choicerow.getCells().add(gen.new Cell()); - choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null); - choicerow.getCells().add(c); - if (isMustSupport(tr) && element.getMustSupport()) { - c.addPiece(gen.new Piece(null, " ", null)); - c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); - } - } - if (tr.hasProfile()) { - Cell typeCell = choicerow.getCells().get(3); - typeCell.addPiece(gen.new Piece(null, "(", null)); - boolean first = true; - for (CanonicalType pt : tr.getProfile()) { - if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); - StructureDefinition psd = context.fetchResource(StructureDefinition.class, pt.getValue()); - if (psd == null) - typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); + if (ADD_REFERENCE_TO_TABLE) { + if (tr.getWorkingCode().equals("canonical")) + c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); else - typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); - if (isMustSupport(pt) && element.getMustSupport()) { - typeCell.addPiece(gen.new Piece(null, " ", null)); - typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); + if (!mustSupportMode && isMustSupportDirect(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); } - + c.getPieces().add(gen.new Piece(null, "(", null)); } - typeCell.addPiece(gen.new Piece(null, ")", null)); - } - } - choicerow.getCells().add(gen.new Cell()); - subRows.add(choicerow); + boolean first = true; + for (CanonicalType rt : tr.getTargetProfile()) { + if (!mustSupportMode || allProfilesMustSupport(tr.getTargetProfile()) || isMustSupport(rt)) { + if (!first) + c.getPieces().add(gen.new Piece(null, " | ", null)); + genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue()); + if (!mustSupportMode && isMustSupport(rt) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } + first = false; + } + } + if (first) + c.getPieces().add(gen.new Piece(null, "Any", null)); + + if (ADD_REFERENCE_TO_TABLE) + c.getPieces().add(gen.new Piece(null, ")", null)); + + } else { + StructureDefinition sd = context.fetchTypeDefinition(t); + if (sd == null) { + System.out.println("Unable to find "+t); + sd = context.fetchTypeDefinition(t); + } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); + choicerow.getCells().add(gen.new Cell()); + choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); + choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null); + choicerow.getCells().add(c); + if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + } else { + choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); + choicerow.getCells().add(gen.new Cell()); + choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); + choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null); + choicerow.getCells().add(c); + if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } + } + if (tr.hasProfile()) { + Cell typeCell = choicerow.getCells().get(3); + typeCell.addPiece(gen.new Piece(null, "(", null)); + boolean first = true; + for (CanonicalType pt : tr.getProfile()) { + if (!mustSupportMode || allProfilesMustSupport(tr.getProfile()) || isMustSupport(pt)) { + if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); + StructureDefinition psd = context.fetchResource(StructureDefinition.class, pt.getValue()); + if (psd == null) + typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); + else + typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); + if (!mustSupportMode && isMustSupport(pt) && element.getMustSupport()) { + typeCell.addPiece(gen.new Piece(null, " ", null)); + typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } + } + } + typeCell.addPiece(gen.new Piece(null, ")", null)); + } + } + choicerow.getCells().add(gen.new Cell()); + subRows.add(choicerow); + } } } @@ -4182,7 +4243,7 @@ public class ProfileUtilities extends TranslatingUtilities { ExtensionContext extDefn = null; genCardinality(gen, element, row, hasDef, used, null); if (hasDef && !"0".equals(element.getMax())) - genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root); + genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, false); else row.getCells().add(gen.new Cell()); generateGridDescription(gen, row, element, null, used.used, null, null, profile, corePath, imagePath, root, null); @@ -5072,6 +5133,9 @@ public class ProfileUtilities extends TranslatingUtilities { if (ref.substring(1, 2).toUpperCase().equals(ref.substring(1,2))) { actual = base+(ref.substring(1)+"."+path.substring(p.length()+1)).substring(prefixLength); path = actual; + } else if (ref.startsWith("http:")) { + actual = base+(ref.substring(ref.indexOf("#")+1)+"."+path.substring(p.length()+1)).substring(prefixLength); + path = actual; } else { // Older versions of FHIR (e.g. 2016May) had reference of the style #parameter instead of #Parameters.parameter, so we have to handle that actual = base+(path.substring(0, path.indexOf(".")+1) + ref.substring(1)+"."+path.substring(p.length()+1)).substring(prefixLength); @@ -6365,6 +6429,25 @@ public class ProfileUtilities extends TranslatingUtilities { return grp; } + public static boolean allTypesMustSupport(ElementDefinition e) { + boolean all = true; + boolean any = false; + for (TypeRefComponent tr : e.getType()) { + all = all && isMustSupport(tr); + any = any || isMustSupport(tr); + } + return !all && !any; + } + + public static boolean allProfilesMustSupport(List profiles) { + boolean all = true; + boolean any = false; + for (CanonicalType u : profiles) { + all = all && isMustSupport(u); + any = any || isMustSupport(u); + } + return !all && !any; + } public static boolean isMustSupportDirect(TypeRefComponent tr) { return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); } 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 5d0d4a1ad..ead0cf012 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 @@ -142,7 +142,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { rows.add(r); boolean hasExt = false; - r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay()); + r.setIcon("icon-q-"+i.getType().toCode().toLowerCase()+".png", i.getType().getDisplay()); r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null)); String txt = (i.hasPrefix() ? i.getPrefix() + ". " : "") + i.getText(); r.getCells().add(gen.new Cell(null, null, txt, null, null)); @@ -361,7 +361,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { rows.add(r); boolean hasExt = false; - r.setIcon("icon-q-"+i.getType().toCode()+".png", i.getType().getDisplay()); + r.setIcon("icon-q-"+i.getType().toCode().toLowerCase()+".png", i.getType().getDisplay()); r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null)); Cell defn = gen.new Cell(); r.getCells().add(defn); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index 3a4015afa..af62c7fb4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -523,14 +523,13 @@ public class ValueSetCheckerSimple implements ValueSetChecker { } else { if (vsi.hasFilter()) { boolean ok = true; - for (ConceptSetFilterComponent f : vsi.getFilter()) + for (ConceptSetFilterComponent f : vsi.getFilter()) { if (!codeInFilter(cs, system, f, code)) { ok = false; break; } - if (ok) { - return true; } + return ok; } List list = cs.getConcept(); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java index 87fd9b28f..fdb9338c2 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java @@ -41,6 +41,7 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.json.JSONUtil; +import org.hl7.fhir.utilities.json.JsonTrackingParser; import org.hl7.fhir.utilities.npm.NpmPackage.NpmPackageFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -549,7 +550,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple @Override public String getPackageId(String canonicalUrl) throws IOException { - String retVal = super.getPackageId(canonicalUrl); + String retVal = findCanonicalInLocalCache(canonicalUrl); + + retVal = super.getPackageId(canonicalUrl); if (retVal == null) { retVal = getPackageIdFromBuildList(canonicalUrl); @@ -559,6 +562,21 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple } + public String findCanonicalInLocalCache(String canonicalUrl) { + try { + for (String pf : listPackages()) { + if (new File(Utilities.path(cacheFolder, pf, "package", "package.json")).exists()) { + JsonObject npm = JsonTrackingParser.parseJsonFile(Utilities.path(cacheFolder, pf, "package", "package.json")); + if (canonicalUrl.equals(JSONUtil.str(npm, "canonical"))) { + return JSONUtil.str(npm, "name"); + } + } + } + } catch (IOException e) { + } + return null; + } + // ========================= Package Mgmt API ======================================================================= private String getPackageIdFromBuildList(String canonical) throws IOException { 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 904ec8dca..f76fa0f8d 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 @@ -25,7 +25,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { void loadPackage(String id, String ver) throws IOException, FHIRException; } - private BasePackageCacheManager pcm; + private FilesystemPackageCacheManager pcm; private IWorkerContext context; private IPackageInstaller installer; @@ -51,19 +51,37 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { if (!Utilities.isAbsoluteUrl(url)) { return false; } - // if we've got to here, it's a reference to a FHIR URL. We're going to try to resolve it on the fly - // first possibility: it's a reference to a version specific URL http://hl7.org/fhir/X.X/... - VersionURLInfo vu = VersionUtilities.parseVersionUrl(url); - if (vu != null) { - NpmPackage pi = pcm.loadPackage(VersionUtilities.packageForVersion(vu.getVersion()), VersionUtilities.getCurrentVersion(vu.getVersion())); - return pi.hasCanonical(vu.getUrl()); + // if we've got to here, it's a reference to a FHIR URL. We're going to try to resolve it on the fly + String pid = null; + String ver = null; + String base = findBaseUrl(url); + if (base == null) { + return false; + } + + if (base.equals("http://terminology.hl7.org")) { + pid = "hl7.terminology"; + } else if (url.startsWith("http://hl7.org/fhir")) { + pid = pcm.getPackageId(base); + } else { + pid = pcm.findCanonicalInLocalCache(base); + } + ver = url.contains("|") ? url.substring(url.indexOf("|")+1) : null; + if (pid == null) { + return false; + } + + if (url.startsWith("http://hl7.org/fhir")) { + // first possibility: it's a reference to a version specific URL http://hl7.org/fhir/X.X/... + VersionURLInfo vu = VersionUtilities.parseVersionUrl(url); + if (vu != null) { + NpmPackage pi = pcm.loadPackage(VersionUtilities.packageForVersion(vu.getVersion()), VersionUtilities.getCurrentVersion(vu.getVersion())); + return pi.hasCanonical(vu.getUrl()); + } } // ok maybe it's a reference to a package we know - String base = findBaseUrl(url); - String pid = pcm.getPackageId(base); - String ver = url.contains("|") ? url.substring(url.indexOf("|")+1) : null; if (pid != null) { if (installer.packageExists(pid, ver)) { installer.loadPackage(pid, ver); @@ -72,13 +90,8 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { } } - if (!url.startsWith("http://hl7.org/fhir")) { - return true; // we don't bother with those in the standalone validator - we assume they are valid - } - - // we assume it's invalid at this point - return false; - + // we don't bother with urls outside fhir space in the standalone validator - we assume they are valid + return !url.startsWith("http://hl7.org/fhir"); } private String findBaseUrl(String url) { 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 16417caa8..5f564f143 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 @@ -209,6 +209,7 @@ public class ValidationService { FhirPublication ver = FhirPublication.fromCode(cliContext.getSv()); ValidationEngine validator = new ValidationEngine(definitions, ver, cliContext.getSv(), tt); System.out.println(" - "+validator.getContext().countAllCaches()+" resources ("+tt.milestone()+")"); + validator.loadIg("hl7.terminology", false); System.out.print(" Terminology server " + cliContext.getTxServer()); String txver = validator.setTerminologyServer(cliContext.getTxServer(), cliContext.getTxLog(), ver); System.out.println(" - Version "+txver+" ("+tt.milestone()+")"); diff --git a/pom.xml b/pom.xml index a7a24718a..d19abb3ba 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.1.0 - 1.1.49 + 1.1.50-SNAPSHOT 5.6.2 3.0.0-M4 0.8.5