From bb11af20fdfd52baa9ccc2d6f685e29838297996 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Thu, 5 Mar 2020 22:16:35 +0100 Subject: [PATCH 1/6] improved recursive if scanning ignores --- .../java/org/hl7/fhir/validation/ValidationEngine.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 69b434d3a..cb18d3eb7 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 @@ -436,6 +436,8 @@ public class ValidationEngine implements IValidatorResourceFetcher { File f = new File(Utilities.path(src)); if (f.exists()) { + if(isIgnoreFile(f)) + return null; if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) return loadPackage(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz")); if (f.isDirectory() && new File(Utilities.path(src, "igpack.zip")).exists()) @@ -547,8 +549,10 @@ public class ValidationEngine implements IValidatorResourceFetcher { } private boolean isIgnoreFile(File ff) { - return Utilities.existsInList(ff.getName(), ".DS_Store") || Utilities.existsInList(Utilities.getFileExtension(ff.getName()).toLowerCase(), "md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"); - + if (ff.getName().startsWith(".")|| ff.getAbsolutePath().contains(".git")){ + return true; + } + return Utilities.existsInList(Utilities.getFileExtension(ff.getName()).toLowerCase(), "md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"); } private Map loadPackage(InputStream stream, String name) throws Exception { From 7858ba9717db45c9df5d575aa82dfd0e6f0e8fbf Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Thu, 5 Mar 2020 22:19:19 +0100 Subject: [PATCH 2/6] removed unnecessary check --- .../src/main/java/org/hl7/fhir/validation/ValidationEngine.java | 2 -- 1 file changed, 2 deletions(-) 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 cb18d3eb7..5de2877c0 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 @@ -436,8 +436,6 @@ public class ValidationEngine implements IValidatorResourceFetcher { File f = new File(Utilities.path(src)); if (f.exists()) { - if(isIgnoreFile(f)) - return null; if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) return loadPackage(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz")); if (f.isDirectory() && new File(Utilities.path(src, "igpack.zip")).exists()) From 2050052c956b99a8fdf3574072750dc8bcae9247 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:28:45 +1100 Subject: [PATCH 3/6] fix problem with bad maps from core spec --- .../fhir/r5/conformance/ProfileUtilities.java | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) 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 a9135390a..a467929b7 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 @@ -585,6 +585,13 @@ public class ProfileUtilities extends TranslatingUtilities { else messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); } + // hack around a problem in R4 definitions (somewhere?) + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (ElementDefinitionMappingComponent mm : ed.getMapping()) { + mm.setMap(mm.getMap().trim()); + + } + } if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition ed : derived.getSnapshot().getElement()) { if (!ed.hasBase()) { @@ -2490,15 +2497,18 @@ public class ProfileUtilities extends TranslatingUtilities { for (ElementDefinitionMappingComponent d : base.getMapping()) { found = found || (d.getIdentity().equals(s.getIdentity()) && d.getMap().equals(s.getMap())); } - if (!found) + if (!found) { base.getMapping().add(s); + } } } - else if (trimDifferential) + else if (trimDifferential) { derived.getMapping().clear(); - else - for (ElementDefinitionMappingComponent t : derived.getMapping()) + } else { + for (ElementDefinitionMappingComponent t : derived.getMapping()) { t.setUserData(DERIVATION_EQUALS, true); + } + } } for (ElementDefinitionMappingComponent m : base.getMapping()) { if (m.hasMap()) { @@ -2509,8 +2519,9 @@ public class ProfileUtilities extends TranslatingUtilities { // todo: constraints are cumulative. there is no replacing for (ElementDefinitionConstraintComponent s : base.getConstraint()) { s.setUserData(IS_DERIVED, true); - if (!s.hasSource()) + if (!s.hasSource()) { s.setSource(srcSD.getUrl()); + } } if (derived.hasConstraint()) { for (ElementDefinitionConstraintComponent s : derived.getConstraint()) { @@ -2521,19 +2532,22 @@ public class ProfileUtilities extends TranslatingUtilities { } } for (IdType id : derived.getCondition()) { - if (!base.hasCondition(id)) + if (!base.hasCondition(id)) { base.getCondition().add(id); + } } // now, check that we still have a bindable type; if not, delete the binding - see task 8477 - if (dest.hasBinding() && !hasBindableType(dest)) + if (dest.hasBinding() && !hasBindableType(dest)) { dest.setBinding(null); + } // finally, we copy any extensions from source to dest for (Extension ex : derived.getExtension()) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, ex.getUrl()); - if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) + if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) { ToolingExtensions.removeExtension(dest, ex.getUrl()); + } dest.addExtension(ex.copy()); } } @@ -2622,18 +2636,21 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean hasBindableType(ElementDefinition ed) { for (TypeRefComponent tr : ed.getType()) { - if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code")) + if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code")) { return true; + } } return false; } private boolean isLargerMax(String derived, String base) { - if ("*".equals(base)) + if ("*".equals(base)) { return false; - if ("*".equals(derived)) + } + if ("*".equals(derived)) { return true; + } return Integer.parseInt(derived) > Integer.parseInt(base); } @@ -2645,10 +2662,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean codesInExpansion(List contains, ValueSetExpansionComponent expansion) { for (ValueSetExpansionContainsComponent cc : contains) { - if (!inExpansion(cc, expansion.getContains())) + if (!inExpansion(cc, expansion.getContains())) { return false; - if (!codesInExpansion(cc.getContains(), expansion)) + } + if (!codesInExpansion(cc.getContains(), expansion)) { return false; + } } return true; } @@ -2656,10 +2675,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean inExpansion(ValueSetExpansionContainsComponent cc, List contains) { for (ValueSetExpansionContainsComponent cc1 : contains) { - if (cc.getSystem().equals(cc1.getSystem()) && cc.getCode().equals(cc1.getCode())) + if (cc.getSystem().equals(cc1.getSystem()) && cc.getCode().equals(cc1.getCode())) { return true; - if (inExpansion(cc, cc1.getContains())) + } + if (inExpansion(cc, cc1.getContains())) { return true; + } } return false; } @@ -2707,24 +2728,28 @@ public class ProfileUtilities extends TranslatingUtilities { private int findEnd(List list, ElementDefinition ed, int cursor) { String path = ed.getPath()+"."; - while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) + while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { cursor++; + } return cursor; } private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { - for (ElementDefinition t : list) - if (t.getPath().equals(ed.getPath())) + for (ElementDefinition t : list) { + if (t.getPath().equals(ed.getPath())) { return t; + } + } return null; } private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { for (int i = start; i < end; i++) { ElementDefinition t = list.get(i); - if (t.getPath().equals(ed.getPath())) + if (t.getPath().equals(ed.getPath())) { return t; + } } return null; } @@ -2732,8 +2757,9 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean isImmediateChild(ElementDefinition ed) { String p = ed.getPath(); - if (!p.contains(".")) + if (!p.contains(".")) { return false; + } p = p.substring(p.indexOf(".")+1); return !p.contains("."); } From 9bbdb8d9b1cbde10b194190c20c1a817fd88e222 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:30:13 +1100 Subject: [PATCH 4/6] Improvements to ValueSet definition rendering --- .../hl7/fhir/r5/utils/NarrativeGenerator.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index cb3c3b669..ae675e911 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -3476,10 +3476,10 @@ public class NarrativeGenerator implements INarrativeGenerator { return false; } - private void generateCopyright(XhtmlNode x, ValueSet vs) { + private void generateCopyright(XhtmlNode x, ValueSet vs) throws FHIRFormatError, DefinitionException, IOException { XhtmlNode p = x.para(); p.b().tx("Copyright Statement:"); - smartAddText(p, " " + vs.getCopyright()); + addMarkdown(x, vs.getCopyright()); } private XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, String lang, List properties) { @@ -3887,18 +3887,22 @@ public class NarrativeGenerator implements INarrativeGenerator { if (vs.hasCopyrightElement()) generateCopyright(x, vs); } - XhtmlNode p = x.para(); - p.tx("This value set includes codes from the following code systems:"); - XhtmlNode ul = x.ul(); - XhtmlNode li; - for (ConceptSetComponent inc : vs.getCompose().getInclude()) { - hasExtensions = genInclude(rcontext, ul, inc, "Include", langs, maps) || hasExtensions; - } - for (ConceptSetComponent exc : vs.getCompose().getExclude()) { - hasExtensions = genInclude(rcontext, ul, exc, "Exclude", langs, maps) || hasExtensions; - } + if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) { + hasExtensions = genInclude(rcontext, ul, vs.getCompose().getInclude().get(0), "Include", langs, maps) || hasExtensions; + } else { + XhtmlNode p = x.para(); + p.tx("This value set includes codes based on the following rules:"); + XhtmlNode li; + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + hasExtensions = genInclude(rcontext, ul, inc, "Include", langs, maps) || hasExtensions; + } + for (ConceptSetComponent exc : vs.getCompose().getExclude()) { + hasExtensions = genInclude(rcontext, ul, exc, "Exclude", langs, maps) || hasExtensions; + } + } + // now, build observed languages if (langs.size() > 0) { @@ -4000,6 +4004,10 @@ public class NarrativeGenerator implements INarrativeGenerator { if (inc.getConcept().size() > 0) { li.addText(type+" these codes as defined in "); addCsRef(inc, li, e); + if (inc.hasVersion()) { + li.addText(" version "); + li.code(inc.getVersion()); + } XhtmlNode t = li.table("none"); boolean hasComments = false; From dcb3e1f7f0865a57113c5bac03cf3fb48321a22a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:30:41 +1100 Subject: [PATCH 5/6] improve documentation --- .../org/hl7/fhir/validation/ValidationEngine.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) 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 4fda919e7..4e0f3b622 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 @@ -106,19 +106,15 @@ POSSIBILITY OF SUCH DAMAGE. * * 1/ Initialize * ValidationEngine validator = new ValidationEngine(src); - * - this must refer to the igpack.zip for the version of the spec against which you want to validate - * it can be a url or a file reference. It can nominate the igpack.zip directly, - * or it can name the container alone (e.g. just the spec URL). - * The validation engine does not cache igpack.zip. the user must manage that if desired + * - this must be the packageId of the relevant core specification + * for the version you want to validate against (e.g. hl7.fhir.r4.core) * * validator.connectToTSServer(txServer); * - this is optional; in the absence of a terminology service, snomed, loinc etc will not be validated * * validator.loadIg(src); - * - call this any number of times for the Implementation Guide(s) of interest. This is a reference - * to the igpack.zip for the implementation guide - same rules as above - * the version of the IGPack must match that of the spec - * Alternatively it can point to a local folder that contains conformance resources. + * - call this any number of times for the Implementation Guide(s) of interest. + * - See https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator for documentation about the src parameter (-ig parameter) * * validator.loadQuestionnaire(src) * - url or filename of a questionnaire to load. Any loaded questionnaires will be used while validating @@ -136,7 +132,8 @@ POSSIBILITY OF SUCH DAMAGE. * if the source is provided as byte[] or stream, you need to provide a format too, though you can * leave that as null, and the validator will guess * - * 3. Or, instead of validating, transform + * 3. Or, instead of validating, transform (see documentation and use in Validator.java) + * * @author Grahame Grieve * */ From fe188da8bdc6df074b37551ec5cba14739af3e97 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 16:14:00 +1100 Subject: [PATCH 6/6] fix rendering of slicing in profiles --- .../java/org/hl7/fhir/r5/conformance/ProfileUtilities.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 a467929b7..7813013a9 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 @@ -3329,8 +3329,10 @@ public class ProfileUtilities extends TranslatingUtilities { s = "@"+s; String hint = ""; hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : "")); - hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); - hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); + if (hasDef && element.hasDefinition()) { + hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); + hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); + } Cell left = gen.new Cell(null, ref, s, hint, null); row.getCells().add(left); Cell gc = gen.new Cell();