From 5f730916d44eaf7c55d8b92d42b7401aaee02776 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 31 Aug 2020 13:19:15 +1000 Subject: [PATCH 1/8] Fix must support rendering of pattern values --- .../java/org/hl7/fhir/r5/conformance/ProfileUtilities.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 e1e3aa655..6192b70a3 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 @@ -4105,7 +4105,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); } else { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen"))); - genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, mustSupportOnly); + genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); } if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { Piece p = describeCoded(gen, definition.getFixed()); @@ -4119,7 +4119,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); else { c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen"))); - genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, false); + genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly); } } else if (definition.hasExample()) { for (ElementDefinitionExampleComponent ex : definition.getExample()) { @@ -4267,7 +4267,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.addPiece(gen.new Piece("br")); c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen")); - genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, false); + genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, skipnoValue); } } } From 42a7dad39aa58f5fb870b6f1ae4c6d39b60ff5d4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 31 Aug 2020 13:21:28 +1000 Subject: [PATCH 2/8] Add time tracking for progress reporting --- .../fhir/r5/context/BaseWorkerContext.java | 18 +++- .../hl7/fhir/r5/context/IWorkerContext.java | 3 + .../fhir/r5/context/SimpleWorkerContext.java | 10 +- .../org/hl7/fhir/utilities/TimeTracker.java | 101 ++++++++++++++++++ 4 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 8e03f5acd..33fbd0dc2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -109,6 +109,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.OIDUtils; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.ToolingClientLogger; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.Utilities; @@ -200,17 +201,19 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte protected Parameters expParameters; private TranslationServices translator = new NullTranslator(); protected TerminologyCache txCache; - + protected TimeTracker clock; private boolean tlogging = true; public BaseWorkerContext() throws FileNotFoundException, IOException, FHIRException { txCache = new TerminologyCache(lock, null); setValidationMessageLanguage(getLocale()); + clock = new TimeTracker(); } public BaseWorkerContext(Locale locale) throws FileNotFoundException, IOException, FHIRException { txCache = new TerminologyCache(lock, null); setValidationMessageLanguage(locale); + clock = new TimeTracker(); } public BaseWorkerContext(CanonicalResourceManager codeSystems, CanonicalResourceManager valueSets, CanonicalResourceManager maps, CanonicalResourceManager profiles, @@ -221,6 +224,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte this.maps = maps; this.structures = profiles; this.guides = guides; + clock = new TimeTracker(); } protected void copy(BaseWorkerContext other) { @@ -1914,5 +1918,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } - + public TimeTracker clock() { + return clock; + } + + + public int countAllCaches() { + return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() + + guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() + systems.size(); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 0d31f3d3f..8a0593606 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -65,6 +65,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.IResourceValidator; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.cache.BasePackageCacheManager; import org.hl7.fhir.utilities.cache.NpmPackage; @@ -769,4 +770,6 @@ public interface IWorkerContext { public int getClientRetryCount(); public IWorkerContext setClientRetryCount(int value); + + public TimeTracker clock(); } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index d66cee01c..207d6ec29 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -78,6 +78,7 @@ import org.hl7.fhir.r5.terminologies.TerminologyClient; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.utilities.CSFileInputStream; import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.cache.BasePackageCacheManager; @@ -810,9 +811,8 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon return loadedPackages.contains(id+"#"+ver); } - - - - - + public void setClock(TimeTracker tt) { + clock = tt; + + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java new file mode 100644 index 000000000..0ea7c03b6 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java @@ -0,0 +1,101 @@ +package org.hl7.fhir.utilities; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hl7.fhir.utilities.TimeTracker.Counter; + +public class TimeTracker { + + public class Counter { + private String name; + private int count; + private long length; + public Counter(String name) { + this.name = name; + } + } + + public class Session { + private long start = System.nanoTime(); + private String name; + public Session(String name) { + this.name = name; + } + public void end() { + endSession(this); + } + } + + private List sessions = new ArrayList<>(); + private List records = new ArrayList<>(); + private long globalStart; + private long milestone = 0; + + + public TimeTracker() { + super(); + globalStart = System.nanoTime(); + } + + public Session start(String name) { + Counter c = null; + for (Counter t : records) { + if (t.name.equals(name)) { + c = t; + } + } + if (c == null) { + c = new Counter(name); + records.add(c); + } + Session session = new Session(name); + sessions.add(session); + return session; + } + + private void endSession(Session session) { + sessions.remove(session); + Counter c = null; + for (Counter t : records) { + if (t.name.equals(session.name)) { + c = t; + } + } + c.count++; + c.length = c.length + System.nanoTime() - session.start; + } + + + public String report() { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (Counter c : records) { + if (c.count == 1) { + b.append(c.name+": "+Utilities.presentDuration(c.length)); + } + } + for (Counter c : records) { + if (c.count > 1) { + b.append(c.name+": "+Utilities.presentDuration(c.length)+" (#"+c.count+")"); + } + } + return "Times: "+b.toString(); + } + + public String clock() { + return Utilities.presentDuration(System.nanoTime() - globalStart); + } + + public String instant() { + return Utilities.presentDuration(System.nanoTime() - globalStart); + } + + public String milestone() { + long start = milestone == 0 ? globalStart : milestone ; + milestone = System.nanoTime(); + return Utilities.presentDuration(milestone - start); + } + +} From c7e8ffff0990f07f30d1fc3b337913d595247704 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 31 Aug 2020 13:22:42 +1000 Subject: [PATCH 3/8] Add parent tracking for Validation support of nested bundles --- .../org/hl7/fhir/r5/elementmodel/Element.java | 20 +++++++++ .../org/hl7/fhir/utilities/Utilities.java | 27 ++++++++++++ .../instance/InstanceValidator.java | 41 +++++++++++++++---- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index f5593fd81..a222778fe 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 @@ -100,6 +100,8 @@ public class Element extends Base { private SpecialElement special; private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation private String explicitType; // for xsi:type attribute + private Element parentForValidator; + private boolean hasParentForValidator; public Element(String name) { super(); @@ -911,5 +913,23 @@ public class Element extends Base { return false; } + /** + * this is set by the instance validator. There's no reason to maintain this when working with an element tree, and so it should be ignored outside the validator + */ + public Element getParentForValidator() { + if (!hasParentForValidator) { + throw new Error("Parent not set"); + } + return parentForValidator; + } + + public void setParentForValidator(Element parentForValidator) { + this.parentForValidator = parentForValidator; + this.hasParentForValidator = true; + } + + public boolean hasParentForValidator() { + return hasParentForValidator; + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 1b132bcc3..2015c3e22 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -57,6 +57,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -1369,4 +1370,30 @@ public class Utilities { return byteArrays; } + + public static String presentDuration(long duration) { + duration = duration / 1000000; + String res = ""; // ; + long days = TimeUnit.MILLISECONDS.toDays(duration); + long hours = TimeUnit.MILLISECONDS.toHours(duration) - + TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(duration)); + long minutes = TimeUnit.MILLISECONDS.toMinutes(duration) - + TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(duration)); + long seconds = TimeUnit.MILLISECONDS.toSeconds(duration) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)); + long millis = TimeUnit.MILLISECONDS.toMillis(duration) - + TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(duration)); + + if (days > 0) + res = String.format("%dd %02d:%02d:%02d.%04d", days, hours, minutes, seconds, millis); + else if (hours > 0) + res = String.format("%02d:%02d:%02d.%04d", hours, minutes, seconds, millis); + else // + res = String.format("%02d:%02d.%04d", minutes, seconds, millis); +// else +// res = String.format("%02d.%04d", seconds, millis); + return res; + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 94c87257a..48179bc5e 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 @@ -238,19 +238,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (c.getAppContext() instanceof Element) { - Element bnd = (Element) c.getAppContext(); - Base res = resolveInBundle(url, bnd); - if (res != null) { - return res; + Element element = (Element) c.getAppContext(); + while (element != null) { + Base res = resolveInBundle(url, element); + if (res != null) { + return res; + } + element = element.getParentForValidator(); } } Base res = resolveInBundle(url, c.getResource()); if (res != null) { return res; } - res = resolveInBundle(url, c.getContainer()); - if (res != null) { - return res; + Element element = c.getRootResource(); + while (element != null) { + res = resolveInBundle(url, element); + if (res != null) { + return res; + } + element = element.getParentForValidator(); } if (externalHostServices != null) { @@ -279,6 +286,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (item instanceof Resource) { try { Element e = new ObjectConverter(context).convert((Resource) item); + setParents(e); self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, e, validationLanguage)); } catch (IOException e1) { throw new FHIRException(e1); @@ -682,6 +690,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat resourceTracker.clear(); executionId = UUID.randomUUID().toString(); baseOnly = profiles.isEmpty(); + setParents(element); long t = System.nanoTime(); if (profiles == null || profiles.isEmpty()) { @@ -697,6 +706,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.overall(t); } + private void checkElementUsage(List errors, Element element, NodeStack stack) { String elementUsage = element.getUserString("elementSupported"); hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getUrl()); @@ -2418,6 +2428,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat throw new FHIRException(e); } if (ext != null) { + setParents(ext); fetchCache.put(ref, ext); } } @@ -3156,6 +3167,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return fetchCache.get(ref); } else { Element res = fetcher.fetch(appContext, ref); + setParents(res); fetchCache.put(ref, res); return res; } @@ -4903,5 +4915,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.noCheckAggregation = noCheckAggregation; } + + public static void setParents(Element element) { + if (element != null && !element.hasParentForValidator()) { + element.setParentForValidator(null); + setParentsInner(element); + } + } + public static void setParentsInner(Element element) { + for (Element child : element.getChildren()) { + child.setParentForValidator(element); + setParentsInner(child); + } + + } + } \ No newline at end of file From 3fad39620b05c576db73258de671b37197b7f9e6 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 31 Aug 2020 13:23:31 +1000 Subject: [PATCH 4/8] fix rendering of Documents --- .../hl7/fhir/r5/renderers/BundleRenderer.java | 195 ++++++++++++++++-- .../fhir/r5/renderers/ResourceRenderer.java | 3 + .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 8 + 3 files changed, 189 insertions(+), 17 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java index 33fa539ff..614b54695 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java @@ -8,6 +8,7 @@ import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent; @@ -15,8 +16,11 @@ import org.hl7.fhir.r5.model.Bundle.BundleEntryResponseComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent; import org.hl7.fhir.r5.model.Bundle.BundleType; import org.hl7.fhir.r5.model.Composition; +import org.hl7.fhir.r5.model.Composition.SectionComponent; import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.Property; import org.hl7.fhir.r5.model.Provenance; +import org.hl7.fhir.r5.model.Reference; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; @@ -58,8 +62,7 @@ public class BundleRenderer extends ResourceRenderer { if ("document".equals(b.get("type").primitiveValue())) { if (entries.isEmpty() || (entries.get(0).has("resource") && "Composition".equals(entries.get(0).get("resource").fhirType()))) throw new FHIRException("Invalid document - first entry is not a Composition"); - ResourceWrapper r = (ResourceWrapper) entries.get(0).getChildByName("resource").getValues().get(0); - x.addChildren(r.getNarrative()); + return renderDocument(x, b, entries); } else if ("collection".equals(b.get("type").primitiveValue()) && allEntriesAreHistoryProvenance(entries)) { // nothing } else { @@ -69,7 +72,7 @@ public class BundleRenderer extends ResourceRenderer { for (BaseWrapper be : entries) { i++; if (be.has("fullUrl")) { - root.an(makeInternalLink(be.get("fullUrl").primitiveValue())); + root.an(makeInternalBundleLink(be.get("fullUrl").primitiveValue())); } if (be.has("resource") && be.getChildByName("resource").getValues().get(0).has("id")) { root.an(be.get("resource").fhirType() + "_" + be.getChildByName("resource").getValues().get(0).get("id").primitiveValue()); @@ -89,7 +92,17 @@ public class BundleRenderer extends ResourceRenderer { if (be.has("resource")) { root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.get("resource").fhirType())); ResourceWrapper rw = be.getChildByName("resource").getAsResource(); - root.blockquote().addChildren(rw.getNarrative()); + XhtmlNode xn = rw.getNarrative(); + if (xn == null || xn.isEmpty()) { + ResourceRenderer rr = RendererFactory.factory(rw, context); + try { + xn = rr.render(rw); + } catch (Exception e) { + xn = new XhtmlNode(); + xn.para().b().tx("Exception generating narrative: "+e.getMessage()); + } + } + root.blockquote().addChildren(xn); } } } @@ -97,13 +110,154 @@ public class BundleRenderer extends ResourceRenderer { } + private boolean renderDocument(XhtmlNode x, ResourceWrapper b, List entries) throws UnsupportedEncodingException, FHIRException, IOException, EOperationOutcome { + // from the spec: + // + // When the document is presented for human consumption, applications SHOULD present the collated narrative portions in order: + // * The subject resource Narrative + // * The Composition resource Narrative + // * The section.text Narratives + ResourceWrapper comp = (ResourceWrapper) entries.get(0).getChildByName("resource").getValues().get(0); + ResourceWrapper subject = resolveReference(entries, comp.get("subject")); + if (subject != null) { + if (subject.hasNarrative()) { + x.addChildren(subject.getNarrative()); + } else { + RendererFactory.factory(subject, context).render(x, subject); + } + } + x.hr(); + if (comp.hasNarrative()) { + x.addChildren(comp.getNarrative()); + x.hr(); + } + List sections = comp.children("section"); + for (BaseWrapper section : sections) { + addSection(x, section, 2, false); + } + return false; + } + + private void addSection(XhtmlNode x, BaseWrapper section, int level, boolean nested) throws UnsupportedEncodingException, FHIRException, IOException { + if (section.has("title") || section.has("code") || section.has("text") || section.has("section")) { + XhtmlNode div = x.div(); + if (section.has("title")) { + div.h(level).tx(section.get("title").primitiveValue()); + } else if (section.has("code")) { + renderBase(div.h(level), section.get("code")); + } + if (section.has("text")) { + Base narrative = section.get("text"); + x.addChildren(narrative.getXhtml()); + } + if (section.has("section")) { + List sections = section.children("section"); + for (BaseWrapper child : sections) { + if (nested) { + addSection(x.blockquote(), child, level+1, true); + } else { + addSection(x, child, level+1, true); + } + } + } + } + // children + } + + private ResourceWrapper resolveReference(List entries, Base base) throws UnsupportedEncodingException, FHIRException, IOException { + Property prop = base.getChildByName("reference"); + if (prop.hasValues()) { + String ref = prop.getValues().get(0).primitiveValue(); + if (ref != null) { + for (BaseWrapper entry : entries) { + if (entry.has("fullUrl")) { + String fu = entry.get("fullUrl").primitiveValue(); + if (ref.equals(fu)) { + return (ResourceWrapper) entry.getChildByName("resource").getValues().get(0); + } + } + } + } + } + return null; + } + + private boolean renderDocument(XhtmlNode x, Bundle b) throws UnsupportedEncodingException, FHIRException, IOException, EOperationOutcome { + // from the spec: + // + // When the document is presented for human consumption, applications SHOULD present the collated narrative portions in order: + // * The subject resource Narrative + // * The Composition resource Narrative + // * The section.text Narratives + Composition comp = (Composition) b.getEntry().get(0).getResource(); + Resource subject = resolveReference(b, comp.getSubject()); + if (subject != null) { + XhtmlNode nx = (subject instanceof DomainResource) ? ((DomainResource) subject).getText().getDiv() : null; + if (nx != null) { + x.addChildren(nx); + } else { + RendererFactory.factory(subject, context).render(x, subject); + } + } + x.hr(); + if (comp.getText().hasDiv()) { + x.addChildren(comp.getText().getDiv()); + x.hr(); + } + for (SectionComponent section : comp.getSection()) { + addSection(x, section, 2, false); + } + return false; + } + + private Resource resolveReference(Bundle bnd, Reference reference) { + String ref = reference.getReference(); + if (ref == null) { + return null; + } + for (BundleEntryComponent be : bnd.getEntry()) { + if (ref.equals(be.getFullUrl())) { + return be.getResource(); + } + } + return null; + } + + + private void addSection(XhtmlNode x, SectionComponent section, int level, boolean nested) throws UnsupportedEncodingException, FHIRException, IOException { + if (section.hasTitle() || section.hasCode() || section.hasText() || section.hasSection()) { + XhtmlNode div = x.div(); + if (section.hasTitle()) { + div.h(level).tx(section.getTitle()); + } else if (section.hasCode()) { + renderBase(div.h(level), section.getCode()); + } + if (section.hasText()) { + x.addChildren(section.getText().getDiv()); + } + if (section.hasSection()) { + List sections = section.getSection(); + for (SectionComponent child : sections) { + if (nested) { + addSection(x.blockquote(), child, level+1, true); + } else { + addSection(x, child, level+1, true); + } + } + } + } + // children + } + + public XhtmlNode render(Bundle b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { if (b.getType() == BundleType.DOCUMENT) { if (!b.hasEntry() || !(b.getEntryFirstRep().hasResource() && b.getEntryFirstRep().getResource() instanceof Composition)) { throw new FHIRException("Invalid document - first entry is not a Composition"); } - Composition dr = (Composition) b.getEntryFirstRep().getResource(); - return dr.getText().getDiv(); + XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); + renderDocument(x, b); + return x; } else if ((b.getType() == BundleType.COLLECTION && allEntresAreHistoryProvenance(b))) { return null; } else { @@ -113,7 +267,7 @@ public class BundleRenderer extends ResourceRenderer { for (BundleEntryComponent be : b.getEntry()) { i++; if (be.hasFullUrl()) - root.an(makeInternalLink(be.getFullUrl())); + root.an(makeInternalBundleLink(be.getFullUrl())); if (be.hasResource() && be.getResource().hasId()) root.an(be.getResource().getResourceType().name() + "_" + be.getResource().getId()); root.hr(); @@ -130,10 +284,22 @@ public class BundleRenderer extends ResourceRenderer { renderResponse(root, be.getResponse()); if (be.hasResource()) { root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.getResource().fhirType())); - if (be.hasResource() && be.getResource() instanceof DomainResource) { - DomainResource dr = (DomainResource) be.getResource(); - if ( dr.getText().hasDiv()) - root.blockquote().getChildNodes().addAll(checkInternalLinks(b, dr.getText().getDiv().getChildNodes())); + if (be.hasResource()) { + XhtmlNode xn = null; + if (be.getResource() instanceof DomainResource) { + DomainResource dr = (DomainResource) be.getResource(); + xn = dr.getText().getDiv(); + } + if (xn == null || xn.isEmpty()) { + ResourceRenderer rr = RendererFactory.factory(be.getResource(), context); + try { + xn = rr.build(be.getResource()); + } catch (Exception e) { + xn = new XhtmlNode(); + xn.para().b().tx("Exception generating narrative: "+e.getMessage()); + } + } + root.blockquote().getChildNodes().addAll(checkInternalLinks(b, xn.getChildNodes())); } } } @@ -182,7 +348,7 @@ public class BundleRenderer extends ResourceRenderer { } } if (fix) { - n.setAttribute("href", "#"+makeInternalLink(n.getAttribute("href"))); + n.setAttribute("href", "#"+makeInternalBundleLink(n.getAttribute("href"))); } } @@ -228,11 +394,6 @@ public class BundleRenderer extends ResourceRenderer { } - - private String makeInternalLink(String fullUrl) { - return fullUrl.replace(":", "-"); - } - public String display(Bundle bundle) throws UnsupportedEncodingException, IOException { return "??"; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index 61fd75c3f..53db3f5ac 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -324,5 +324,8 @@ public abstract class ResourceRenderer extends DataRenderer { } } + public static String makeInternalBundleLink(String fullUrl) { + return fullUrl.replace(":", "-"); + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index 67c998c4b..419c88212 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseXhtml; import org.hl7.fhir.utilities.Utilities; @@ -476,6 +477,13 @@ public class XhtmlNode implements IBaseXhtml { return addTag("h2"); } + public XhtmlNode h(int level) { + if (level < 1 || level > 6) { + throw new FHIRException("Illegal Header level "+level); + } + return addTag("h"+Integer.toString(level)); + } + public XhtmlNode h3() { return addTag("h3"); } From ef2ad7ce21c9fceecc75c05252ab4c2d5119bae3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 31 Aug 2020 13:24:20 +1000 Subject: [PATCH 5/8] rework validation output + fix up package loading in validator --- .../cache/FilesystemPackageCacheManager.java | 33 +++++++- .../fhir/utilities/cache/PackageHacker.java | 10 +-- .../hl7/fhir/validation/ValidationEngine.java | 79 ++++++++++++------- .../org/hl7/fhir/validation/Validator.java | 25 +++--- .../hl7/fhir/validation/cli/ValidatorGui.java | 2 +- .../services/StandAloneValidatorFetcher.java | 15 ++-- .../cli/services/ValidationService.java | 58 +++++++++----- .../hl7/fhir/validation/cli/utils/Common.java | 11 ++- .../fhir/validation/cli/utils/Display.java | 4 +- .../instance/utils/ValidatorHostContext.java | 25 ++---- 10 files changed, 163 insertions(+), 99 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java index 9ee07dd02..bf9bdac65 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java @@ -90,7 +90,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple // private static final String SECONDARY_SERVER = "http://local.fhir.org:960/packages"; public static final String PACKAGE_REGEX = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+$"; public static final String PACKAGE_VERSION_REGEX = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+\\#[a-z0-9\\-\\_]+(\\.[a-z0-9\\-\\_]+)*$"; - public static final String PACKAGE_VERSION_REGEX_OPT = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+(\\#[a-z0-9\\-\\_]+(\\.[a-z0-9\\-\\_]+)*)$"; + public static final String PACKAGE_VERSION_REGEX_OPT = "^[a-z][a-z0-9\\_\\-]*(\\.[a-z0-9\\_\\-]+)+(\\#[a-z0-9\\-\\_]+(\\.[a-z0-9\\-\\_]+)*)?$"; private static final Logger ourLog = LoggerFactory.getLogger(FilesystemPackageCacheManager.class); private static final String CACHE_VERSION = "3"; // second version - see wiki page private String cacheFolder; @@ -862,7 +862,38 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple } } + public boolean packageExists(String id, String ver) throws IOException { + if (packageInstalled(id, ver)) { + return true; + } + for (String s : getPackageServers()) { + if (new PackageClient(s).exists(id, ver)) { + return true; + } + } + return false; + } + public boolean packageInstalled(String id, String version) { + for (NpmPackage p : temporaryPackages) { + if (p.name().equals(id) && ("current".equals(version) || "dev".equals(version) || p.version().equals(version))) { + return true; + } + if (p.name().equals(id) && Utilities.noString(version)) { + return true; + } + } + + for (String f : sorted(new File(cacheFolder).list())) { + if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) { + return true; + } + } + if ("dev".equals(version)) + return packageInstalled(id, "current"); + else + return false; + } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java index f855b8f79..ba3319bbe 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java @@ -25,7 +25,7 @@ import com.google.gson.JsonObject; public class PackageHacker { public static void main(String[] args) throws FileNotFoundException, IOException { - new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\2020Sep\\hl7.fhir.r5.expansions.tgz"); + new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\uv\\cdisc-lab\\package.tgz"); } private void edit(String name) throws FileNotFoundException, IOException { @@ -57,10 +57,10 @@ public class PackageHacker { private void change(JsonObject npm, Map content) throws FileNotFoundException, IOException { fixVersions(npm); -// npm.remove("url"); -// npm.addProperty("url", url); - npm.remove("version"); - npm.addProperty("version", "4.5.0"); + npm.remove("url"); + npm.addProperty("url", "http://hl7.org/fhir/uv/cdisc-lab/STU1"); + npm.remove("name"); + npm.addProperty("name", "hl7.fhir.uv.cdisc-lab"); // npm.remove("canonical"); // npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary"); //// npm.remove("description"); 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 6cd403338..58de76e86 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 @@ -38,6 +38,7 @@ import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageI import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.cache.NpmPackage; @@ -351,8 +352,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst context = SimpleWorkerContext.fromNothing(); } - public void setTerminologyServer(String src, String log, FhirPublication version) throws FHIRException, URISyntaxException { - connectToTSServer(src, log, version); + public String setTerminologyServer(String src, String log, FhirPublication version) throws FHIRException, URISyntaxException { + return connectToTSServer(src, log, version); } public boolean isHintAboutNonMustSupport() { @@ -382,7 +383,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, boolean canRunWithoutTerminologyServer, String vString) throws FHIRException, IOException, URISyntaxException { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); - loadCoreDefinitions(src, false); + loadCoreDefinitions(src, false, null); context.setCanRunWithoutTerminology(canRunWithoutTerminologyServer); setTerminologyServer(txsrvr, txLog, version); this.version = vString; @@ -390,13 +391,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, String vString) throws FHIRException, IOException, URISyntaxException { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); - loadCoreDefinitions(src, false); + loadCoreDefinitions(src, false, null); setTerminologyServer(txsrvr, txLog, version); this.version = vString; } + public ValidationEngine(String src, FhirPublication version, String vString, TimeTracker tt) throws FHIRException, IOException, URISyntaxException { + pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); + loadCoreDefinitions(src, false, tt); + this.version = vString; + } + + public ValidationEngine(String src) throws FHIRException, IOException { - loadCoreDefinitions(src, false); + loadCoreDefinitions(src, false, null); pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); } @@ -408,7 +416,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst this.language = language; } - private void loadCoreDefinitions(String src, boolean recursive) throws FHIRException, IOException { + private void loadCoreDefinitions(String src, boolean recursive, TimeTracker tt) throws FHIRException, IOException { if (pcm == null) { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); } @@ -427,6 +435,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst context.setCacheId(UUID.randomUUID().toString()); context.setAllowLoadingDuplicates(true); // because of Forge context.setExpansionProfile(makeExpProfile()); + if (tt != null) { + context.setClock(tt); + } NpmPackage npmX = pcm.loadPackage("hl7.fhir.xver-extensions", "0.0.4"); context.loadFromPackage(npmX, null); } @@ -912,16 +923,17 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return checkIsResource(TextFile.fileToBytes(path), path); } - public void connectToTSServer(String url, String log, FhirPublication version) throws URISyntaxException, FHIRException { + public String connectToTSServer(String url, String log, FhirPublication version) throws URISyntaxException, FHIRException { context.setTlogging(false); if (url == null) { context.setCanRunWithoutTerminology(true); + return "n/a: No Terminology Server"; } else { try { - context.connectToTSServer(TerminologyClientFactory.makeClient(url, version), log); + return context.connectToTSServer(TerminologyClientFactory.makeClient(url, version), log); } catch (Exception e) { if (context.isCanRunWithoutTerminology()) { - System.out.println("Running without Terminology Server (error: "+e.getMessage()+")"); + return "n/a: Running without Terminology Server (error: "+e.getMessage()+")"; } else throw e; } @@ -956,9 +968,16 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } } - context.loadFromPackage(npm, loaderForVersion(npm.fhirVersion())); + System.out.print(" Load " + src); + if (!src.contains("#")) { + System.out.print("#"+npm.version()); + } + int count = context.loadFromPackage(npm, loaderForVersion(npm.fhirVersion())); + System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); } else { + System.out.print(" Load " + src); String canonical = null; + int count = 0; Map source = loadIgSource(src, recursive, true); String version = Constants.VERSION; if (this.version != null) { @@ -972,6 +991,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (!exemptFile(fn)) { Resource r = loadFileWithErrorChecking(version, t, fn); if (r != null) { + count++; context.cacheResource(r); if (r instanceof ImplementationGuide) { canonical = ((ImplementationGuide) r).getUrl(); @@ -988,6 +1008,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (canonical != null) { grabNatives(source, canonical); } + System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); } } @@ -1247,22 +1268,27 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } public Resource validate(List sources, List profiles) throws FHIRException, IOException { + if (profiles.size() > 0) { + System.out.println(" Profiles: "+profiles); + } List refs = new ArrayList(); boolean asBundle = handleSources(sources, refs); Bundle results = new Bundle(); results.setType(Bundle.BundleType.COLLECTION); for (String ref : refs) { + TimeTracker.Session tts = context.clock().start("validation"); + context.clock().milestone(); + System.out.print(" Validate " + ref); Content cnt = loadContent(ref, "validate"); - if (refs.size() > 1) - System.out.println("Validate "+ref); try { OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles); ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref); - if (refs.size() > 1) - produceValidationSummary(outcome); + System.out.println(" " + context.clock().milestone()); results.addEntry().setResource(outcome); + tts.end(); } catch (Exception e) { System.out.println("Validation Infrastructure fail validating "+ref+": "+e.getMessage()); + tts.end(); throw new FHIRException(e); } } @@ -1271,14 +1297,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst else return results.getEntryFirstRep().getResource(); } - - private void produceValidationSummary(OperationOutcome oo) { - for (OperationOutcomeIssueComponent iss : oo.getIssue()) { - if (iss.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR || iss.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.FATAL) { - System.out.println(" "+iss.getSeverity().toCode()+": "+iss.getDetails().getText()); - } - } - } + public OperationOutcome validateString(String location, String source, FhirFormat format, List profiles) throws FHIRException, IOException, EOperationOutcome, SAXException { return validate(location, source.getBytes(), format, profiles); @@ -2361,14 +2380,6 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return TextFile.streamToBytes(c.getInputStream()); } - public void doneLoading(long loadStart) { - if (showTimes) { - String s = String.format("Load Time (ms): %d", (System.nanoTime() - loadStart) / 1000000); - System.out.println(s); - } - - } - public FilesystemPackageCacheManager getPcm() { return pcm; } @@ -2377,5 +2388,13 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return bundleValidationRules; } + public boolean packageExists(String id, String ver) throws IOException, FHIRException { + return pcm.packageExists(id, ver); + } + + public void loadPackage(String id, String ver) throws IOException, FHIRException { + loadIg(id+(ver == null ? "" : "#"+ver), true); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java index 54933851f..8b6a38bf3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java @@ -63,6 +63,7 @@ import java.net.URI; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation; @@ -131,10 +132,11 @@ public class Validator { } public static void main(String[] args) throws Exception { + TimeTracker tt = new TimeTracker(); + TimeTracker.Session tts = tt.start("Loading"); - long loadStart = System.nanoTime(); System.out.println("FHIR Validation tool " + VersionUtil.getVersionString()); - System.out.println("Detected Java version: " + System.getProperty("java.version") + " from " + System.getProperty("java.home") + " on " + System.getProperty("os.arch") + " (" + System.getProperty("sun.arch.data.model") + "bit). " + toMB(Runtime.getRuntime().maxMemory()) + "MB available"); + System.out.println(" Java: " + System.getProperty("java.version") + " from " + System.getProperty("java.home") + " on " + System.getProperty("os.arch") + " (" + System.getProperty("sun.arch.data.model") + "bit). " + toMB(Runtime.getRuntime().maxMemory()) + "MB available"); String proxy = getNamedParam(args, Params.PROXY); if (!Utilities.noString(proxy)) { String[] p = proxy.split("\\:"); @@ -146,7 +148,7 @@ public class Validator { cliContext = Params.loadCliContext(args); String v = Common.getVersion(args); String definitions = VersionUtilities.packageForVersion(v) + "#" + v; - ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog()); + ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog(), null); ValidatorGui.start(cliContext, validationEngine, true); } else if (Params.hasParam(args, Params.TEST)) { Common.runValidationEngineTests(); @@ -167,7 +169,7 @@ public class Validator { } String v = VersionUtilities.getCurrentVersion(cliContext.getSv()); String definitions = VersionUtilities.packageForVersion(v) + "#" + v; - ValidationEngine validator = ValidationService.getValidator(cliContext, definitions); + ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); ComparisonService.doLeftRightComparison(args, dest, validator); } } else { @@ -178,9 +180,11 @@ public class Validator { cliContext.setSv(determineVersion(cliContext)); } + System.out.println("Loading"); // Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). Version gets spit out a couple of lines later after we've loaded the context String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); - ValidationEngine validator = ValidationService.getValidator(cliContext, definitions); + ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); + tts.end(); if (cliContext.getMode() == EngineMode.VERSION) { ValidationService.transformVersion(cliContext, validator); @@ -194,22 +198,21 @@ public class Validator { ValidationService.convertSources(cliContext, validator); } else if (cliContext.getMode() == EngineMode.FHIRPATH) { ValidationService.evaluateFhirpath(cliContext, validator); - } else { - if (definitions == null) { - throw new Exception("Must provide a defn when doing validation"); - } + } else { for (String s : cliContext.getProfiles()) { if (!validator.getContext().hasResource(StructureDefinition.class, s) && !validator.getContext().hasResource(ImplementationGuide.class, s)) { - System.out.println("Fetch Profile from " + s); + System.out.println(" Fetch Profile from " + s); validator.loadProfile(cliContext.getLocations().getOrDefault(s, s)); } } + System.out.println("Validating"); if (cliContext.getMode() == EngineMode.SCAN) { ValidationService.validateScan(cliContext, validator); } else { - ValidationService.validateSources(cliContext, validator, loadStart); + ValidationService.validateSources(cliContext, validator); } } + System.out.println("Done. "+tt.report()); } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/ValidatorGui.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/ValidatorGui.java index d1f8829a2..35a423a13 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/ValidatorGui.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/ValidatorGui.java @@ -28,7 +28,7 @@ public class ValidatorGui { CliContext cliContext = new CliContext(); String v = Common.getVersion(args); String definitions = VersionUtilities.packageForVersion(v) + "#" + v; - ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog()); + ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog(), null); start(new CliContext(), validationEngine, false); } 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 90e24d387..9626987e7 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 @@ -22,7 +22,8 @@ import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageI public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { public interface IPackageInstaller { - public void loadIg(String src, boolean recursive) throws IOException, FHIRException; + public boolean packageExists(String id, String ver) throws IOException, FHIRException; + public void loadPackage(String id, String ver) throws IOException, FHIRException; } private BasePackageCacheManager pcm; @@ -63,13 +64,13 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { // ok maybe it's a reference to a package we know String base = findBaseUrl(url); String pid = pcm.getPackageId(base); - if (url.contains("|")) { - pid = pid+"#"+url.substring(url.indexOf("|")+1); - } + String ver = url.contains("|") ? url.substring(url.indexOf("|")+1) : null; if (pid != null) { - installer.loadIg(pid, false); - NpmPackage pi = pcm.loadPackage(pid); - return pi.hasCanonical(url); + if (installer.packageExists(pid, ver)) { + installer.loadPackage(pid, ver); + NpmPackage pi = pcm.loadPackage(pid); + return pi.hasCanonical(url); + } } if (!url.startsWith("http://hl7.org/fhir")) { 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 b4fb2ed7c..2ce05143a 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 @@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation; @@ -36,7 +37,6 @@ public class ValidationService { } else { System.out.println(" .. validate " + request.listSourceFiles()); } - validator.prepare(); // generate any missing snapshots ValidationResponse response = new ValidationResponse(); for (FileInfo fp : request.getFilesToValidate()) { @@ -62,22 +62,19 @@ public class ValidationService { ve.scanForVersions(cliContext.getSources(), versions); return versions; } - public static void validateSources(CliContext cliContext, ValidationEngine validator, long loadStart) throws Exception { - validator.doneLoading(loadStart); - if (cliContext.getProfiles().size() > 0) { - System.out.println(" .. validate " + cliContext.getSources() + " against " + cliContext.getProfiles().toString()); - } else { - System.out.println(" .. validate " + cliContext.getSources()); - } - validator.prepare(); // generate any missing snapshots + + public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception { Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles()); int ec = 0; + System.out.println("Done. "+validator.getContext().clock().report()); + System.out.println(); + if (cliContext.getOutput() == null) { if (r instanceof Bundle) for (Bundle.BundleEntryComponent e : ((Bundle) r).getEntry()) - ec = displayOperationOutcome((OperationOutcome) e.getResource()) + ec; + ec = ec + displayOperationOutcome((OperationOutcome) e.getResource(), ((Bundle) r).getEntry().size() > 1) + ec; else - ec = displayOperationOutcome((OperationOutcome) r); + ec = displayOperationOutcome((OperationOutcome) r, false); } else { IParser x; if (cliContext.getOutput() != null && cliContext.getOutput().endsWith(".json")) { @@ -193,16 +190,20 @@ public class ValidationService { } } - public static ValidationEngine getValidator(CliContext cliContext, String definitions) throws Exception { - System.out.println(" .. FHIR Version " + cliContext.getSv() + ", definitions from " + definitions); - System.out.println(" .. connect to tx server @ " + cliContext.getTxServer()); - ValidationEngine validator = new ValidationEngine(definitions, cliContext.getTxServer(), cliContext.getTxLog(), FhirPublication.fromCode(cliContext.getSv()), cliContext.getSv()); + public static ValidationEngine getValidator(CliContext cliContext, String definitions, TimeTracker tt) throws Exception { + tt.milestone(); + System.out.print(" Load FHIR v" + cliContext.getSv() + " from " + definitions); + FhirPublication ver = FhirPublication.fromCode(cliContext.getSv()); + ValidationEngine validator = new ValidationEngine(definitions, ver, cliContext.getSv(), tt); + System.out.println(" - "+validator.getContext().countAllCaches()+" resources ("+tt.milestone()+")"); + System.out.print(" Terminology server " + cliContext.getTxServer()); + String txver = validator.setTerminologyServer(cliContext.getTxServer(), cliContext.getTxLog(), ver); + System.out.println(" - Version "+txver+" ("+tt.milestone()+")"); validator.setDebug(cliContext.isDoDebug()); - System.out.println(" (v" + validator.getContext().getVersion() + ")"); for (String src : cliContext.getIgs()) { - System.out.println("+ .. load IG from " + src); validator.loadIg(src, cliContext.isRecursive()); } + System.out.print(" Get set... "); validator.setQuestionnaires(cliContext.getQuestionnaires()); validator.setNative(cliContext.isDoNative()); validator.setHintAboutNonMustSupport(cliContext.isHintAboutNonMustSupport()); @@ -218,10 +219,13 @@ public class ValidationService { validator.setFetcher(new StandAloneValidatorFetcher(validator.getPcm(), validator.getContext(), validator)); validator.getBundleValidationRules().addAll(cliContext.getBundleValidationRules()); TerminologyCache.setNoCaching(cliContext.isNoInternalCaching()); + validator.prepare(); // generate any missing snapshots + System.out.println(" go ("+tt.milestone()+")"); + return validator; } - public static int displayOperationOutcome(OperationOutcome oo) { + public static int displayOperationOutcome(OperationOutcome oo, boolean hasMultiples) { int error = 0; int warn = 0; int info = 0; @@ -235,12 +239,24 @@ public class ValidationService { else info++; } - - System.out.println((error == 0 ? "Success..." : "*FAILURE* ") + "validating " + file + ": " + " error:" + Integer.toString(error) + " warn:" + Integer.toString(warn) + " info:" + Integer.toString(info)); + + if (hasMultiples) { + System.out.print("-- "); + System.out.print(file); + System.out.print(" --"); + System.out.println(Utilities.padLeft("", '-', Integer.max(38, file.length()+6))); + } + System.out.println((error == 0 ? "Success" : "*FAILURE*") + ": " + Integer.toString(error) + " errors, " + Integer.toString(warn) + " warnings, " + Integer.toString(info)+" notes"); for (OperationOutcome.OperationOutcomeIssueComponent issue : oo.getIssue()) { System.out.println(getIssueSummary(issue)); } - System.out.println(); + if (hasMultiples) { + System.out.print("---"); + System.out.print(Utilities.padLeft("", '-', file.length())); + System.out.print("---"); + System.out.println(Utilities.padLeft("", '-', Integer.max(38, file.length()+6))); + System.out.println(); + } return error; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java index 2b2aa28a5..babbb6a03 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java @@ -2,6 +2,7 @@ package org.hl7.fhir.validation.cli.utils; import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.FhirPublication; +import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.validation.ValidationEngine; @@ -80,13 +81,15 @@ public class Common { /** * Default validation engine will point to "http://tx.fhir.org" terminology server. */ - public static ValidationEngine getValidationEngine(String version, String definitions, String txLog) throws Exception { - return getValidationEngine(version, DEFAULT_TX_SERVER, definitions, txLog); + public static ValidationEngine getValidationEngine(String version, String definitions, String txLog, TimeTracker tt) throws Exception { + return getValidationEngine(version, DEFAULT_TX_SERVER, definitions, txLog, tt); } - public static ValidationEngine getValidationEngine(String version, String txServer, String definitions, String txLog) throws Exception { + public static ValidationEngine getValidationEngine(String version, String txServer, String definitions, String txLog, TimeTracker tt) throws Exception { System.out.println("Loading (v = " + version + ", tx server -> " + txServer + ")"); - return new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(version), version); + ValidationEngine ve = new ValidationEngine(definitions, FhirPublication.fromCode(version), version, tt); + ve.connectToTSServer(txServer, txLog, FhirPublication.fromCode(version)); + return ve; } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java index a3ad4b07c..a57040513 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java @@ -15,12 +15,12 @@ import java.io.IOException; public class Display { public static void printCliArgumentsAndInfo(String[] args) throws IOException { - System.out.print("Arguments:"); + System.out.println(" Paths: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder()); + System.out.print(" Params:"); for (String s : args) { System.out.print(s.contains(" ") ? " \"" + s + "\"" : " " + s); } System.out.println(); - System.out.println("Directories: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder()); } public static void displayHelpDetails() { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java index 4373a59f8..523a01a23 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java @@ -4,16 +4,21 @@ import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.utilities.validation.ValidationMessage; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Stack; public class ValidatorHostContext { private Object appContext; - private Element container; // bundle, or parameters - private Element resource; + + // the resource we are actually validating right now + private Element resource; + // the resource that is the scope of id resolution - either the same as resource, or the resource the contains that resource. This can only be one level deep. private Element rootResource; + private StructureDefinition profile; // the profile that contains the content being validated private boolean checkSpecials = true; private Map> sliceRecords; @@ -26,6 +31,7 @@ public class ValidatorHostContext { this.appContext = appContext; this.resource = element; this.rootResource = element; + // no container } public Object getAppContext() { @@ -37,15 +43,6 @@ public class ValidatorHostContext { return this; } - public Element getContainer() { - return container; - } - - public ValidatorHostContext setContainer(Element container) { - this.container = container; - return this; - } - public ValidatorHostContext setResource(Element resource) { this.resource = resource; return this; @@ -98,7 +95,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.rootResource = resource; res.resource = element; - res.container = resource; res.profile = profile; return res; } @@ -107,7 +103,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.rootResource = element; res.resource = element; - res.container = resource; res.profile = profile; return res; } @@ -116,7 +111,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.resource = resource; res.rootResource = rootResource; - res.container = container; res.profile = profile; res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap>(); return res; @@ -126,7 +120,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.resource = resource; res.rootResource = resource; - res.container = container; res.profile = profile; res.checkSpecials = false; return res; @@ -136,7 +129,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.resource = resource; res.rootResource = resource; - res.container = resource; res.profile = profile; res.checkSpecials = false; return res; @@ -146,7 +138,6 @@ public class ValidatorHostContext { ValidatorHostContext res = new ValidatorHostContext(appContext); res.resource = resource; res.rootResource = resource; - res.container = resource; res.profile = profile; res.checkSpecials = false; res.sliceRecords = new HashMap>(); From 2867e20a421e90f5a56011d3f1b49e304d675b59 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 1 Sep 2020 09:42:52 +1000 Subject: [PATCH 6/8] Fix AllergyIntolerance conversions & tests --- .../conv10_30/AllergyIntolerance10_30.java | 10 +-- .../conv10_40/AllergyIntolerance10_40.java | 10 +-- .../AllergyIntolerance10_30Test.java | 8 ++ .../AllergyIntolerance10_40Test.java | 10 ++- .../resources/0_allergy_intolerance_10.json | 62 +++++++++----- .../resources/0_allergy_intolerance_30.json | 70 +++++++++++----- .../resources/0_allergy_intolerance_40.json | 84 ++++++++++++++----- .../resources/1_allergy_intolerance_30.json | 70 +++++++++++----- .../resources/1_allergy_intolerance_40.json | 84 ++++++++++++++----- 9 files changed, 286 insertions(+), 122 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30.java index 0baaa0f3f..fcb2bb778 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30.java @@ -12,9 +12,9 @@ public class AllergyIntolerance10_30 { VersionConvertor_10_30.copyDomainResource(src, tgt); for (org.hl7.fhir.dstu2.model.Identifier identifier : src.getIdentifier()) tgt.addIdentifier(VersionConvertor_10_30.convertIdentifier(identifier)); if (src.hasOnset()) - tgt.setOnset(new org.hl7.fhir.dstu3.model.DateTimeType(src.getOnset())); + tgt.setOnset(VersionConvertor_10_30.convertDateTime(src.getOnsetElement())); if (src.hasRecordedDate()) - tgt.setAssertedDate(src.getRecordedDate()); + tgt.setAssertedDateElement(VersionConvertor_10_30.convertDateTime(src.getRecordedDateElement())); if (src.hasRecorder()) tgt.setRecorder(VersionConvertor_10_30.convertReference(src.getRecorder())); if (src.hasPatient()) @@ -40,7 +40,7 @@ public class AllergyIntolerance10_30 { if (src.hasCategory()) tgt.addCategory(org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceCategory.fromCode(src.getCategory().toCode())); if (src.hasLastOccurence()) - tgt.setLastOccurrence(src.getLastOccurence()); + tgt.setLastOccurrenceElement(VersionConvertor_10_30.convertDateTime(src.getLastOccurenceElement())); if (src.hasNote()) tgt.addNote(VersionConvertor_10_30.convertAnnotation(src.getNote())); for (org.hl7.fhir.dstu2.model.AllergyIntolerance.AllergyIntoleranceReactionComponent reaction : src.getReaction()) @@ -63,9 +63,9 @@ public class AllergyIntolerance10_30 { )); for (org.hl7.fhir.dstu2.model.CodeableConcept concept : src.getManifestation()) tgt.addManifestation(VersionConvertor_10_30.convertCodeableConcept(concept)); if (src.hasDescription()) - src.setDescription(src.getDescription()); + tgt.setDescriptionElement(VersionConvertor_10_30.convertString(src.getDescriptionElement())); if (src.hasOnset()) - tgt.setOnset(src.getOnset()); + tgt.setOnsetElement(VersionConvertor_10_30.convertDateTime(src.getOnsetElement())); if (src.hasSeverity()) tgt.setSeverity(org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceSeverity.fromCode(src.getSeverity().toCode())); if (src.hasExposureRoute()) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40.java index 0f9760ff9..e5c51c648 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40.java @@ -13,7 +13,7 @@ public class AllergyIntolerance10_40 { if (src.hasOnset()) tgt.setOnset(VersionConvertor_10_40.convertType(src.getOnsetElement())); if (src.hasRecordedDate()) - tgt.setRecordedDate(src.getRecordedDate()); + tgt.setRecordedDateElement(VersionConvertor_10_40.convertDateTime(src.getRecordedDateElement())); if (src.hasRecorder()) tgt.setRecorder(VersionConvertor_10_40.convertReference(src.getRecorder())); if (src.hasPatient()) @@ -44,8 +44,8 @@ public class AllergyIntolerance10_40 { tgt.setType(org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceType.fromCode(src.getType().toCode())); if (src.hasCategory()) tgt.addCategory(org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceCategory.fromCode(src.getCategory().toCode())); - if (src.hasLastOccurence()) - tgt.setLastOccurrence(src.getLastOccurence()); + if (src.hasLastOccurenceElement()) + tgt.setLastOccurrenceElement(VersionConvertor_10_40.convertDateTime(src.getLastOccurenceElement())); if (src.hasNote()) tgt.addNote(VersionConvertor_10_40.convertAnnotation(src.getNote())); for (org.hl7.fhir.dstu2.model.AllergyIntolerance.AllergyIntoleranceReactionComponent reaction : src.getReaction()) @@ -68,9 +68,9 @@ public class AllergyIntolerance10_40 { )); for (org.hl7.fhir.dstu2.model.CodeableConcept concept : src.getManifestation()) tgt.addManifestation(VersionConvertor_10_40.convertCodeableConcept(concept)); if (src.hasDescription()) - src.setDescription(src.getDescription()); + tgt.setDescriptionElement(VersionConvertor_10_40.convertString(src.getDescriptionElement())); if (src.hasOnset()) - tgt.setOnset(src.getOnset()); + tgt.setOnsetElement(VersionConvertor_10_40.convertDateTime(src.getOnsetElement())); if (src.hasSeverity()) tgt.setSeverity(org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceSeverity.fromCode(src.getSeverity().toCode())); if (src.hasExposureRoute()) diff --git a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30Test.java b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30Test.java index 920c0df9e..e2c5ab5fd 100644 --- a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30Test.java +++ b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_30/AllergyIntolerance10_30Test.java @@ -36,6 +36,14 @@ public class AllergyIntolerance10_30Test { org.hl7.fhir.dstu3.formats.JsonParser stu_parser = new org.hl7.fhir.dstu3.formats.JsonParser(); org.hl7.fhir.dstu3.model.Resource stu_expected = stu_parser.parse(stu_exepected_input); + if (!stu_expected.equalsDeep(stu_actual)) { + System.out.println("Expected"); + System.out.println(stu_parser.composeString(stu_expected)); + System.out.println(); + System.out.println("Actual"); + System.out.println(stu_parser.composeString(stu_actual)); + } + Assertions.assertTrue(stu_expected.equalsDeep(stu_actual), "Failed comparing\n" + stu_parser.composeString(stu_actual) + "\nand\n" + stu_parser.composeString(stu_expected) ); diff --git a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40Test.java b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40Test.java index e1cf86214..6dc3cba82 100644 --- a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40Test.java +++ b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv10_40/AllergyIntolerance10_40Test.java @@ -36,8 +36,14 @@ public class AllergyIntolerance10_40Test { org.hl7.fhir.r4.formats.JsonParser r4_parser = new org.hl7.fhir.r4.formats.JsonParser(); org.hl7.fhir.r4.model.Resource r4_expected = r4_parser.parse(r4_exepected_input); - Assertions.assertTrue(r4_expected.equalsDeep(r4_actual), - "Failed comparing\n" + r4_parser.composeString(r4_actual) + "\nand\n" + r4_parser.composeString(r4_expected) + if (!r4_expected.equalsDeep(r4_actual)) { + System.out.println("Expected"); + System.out.println(r4_parser.composeString(r4_expected)); + System.out.println(); + System.out.println("Actual"); + System.out.println(r4_parser.composeString(r4_actual)); + } + Assertions.assertTrue(r4_expected.equalsDeep(r4_actual), "Failed comparing\n" + r4_parser.composeString(r4_actual) + "\nand\n" + r4_parser.composeString(r4_expected) ); } } diff --git a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_10.json b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_10.json index 6289fa81f..7c008a863 100644 --- a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_10.json +++ b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_10.json @@ -1,20 +1,42 @@ -{"resourceType": "AllergyIntolerance", - "recordedDate": "2015-08-25T02:11:36", - "status": "confirmed", - "criticality": "CRITL", - "id": "TBwnNbrAqC0Qw5Ha7AFT-2AB", - "onset": "2012-11-07T00:00:00Z", - "recorder": {"display": "MOORE, NICK", - "reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB"}, - "patient": {"display": "Jason Argonaut", - "reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB"}, - "substance": {"text": "PENICILLIN G", - "coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7980", - "display": "PENICILLIN G"}, - {"system": "http://fdasis.nlm.nih.gov", - "code": "Q42T66VG0C", - "display": "PENICILLIN G"}]}, - "reaction": [{"certainty": "confirmed", - "onset": "2012-11-07T00:00:00Z", - "manifestation": [{"text": "Hives"}]}]} \ No newline at end of file +{ + "resourceType" : "AllergyIntolerance", + "recordedDate" : "2015-08-25T02:11:36", + "status" : "confirmed", + "criticality" : "CRITL", + "id" : "TBwnNbrAqC0Qw5Ha7AFT-2AB", + "onset" : "2012-11-07T00:00:00Z", + "recorder" : { + "display" : "MOORE, NICK", + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB" + }, + "patient" : { + "display" : "Jason Argonaut", + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB" + }, + "substance" : { + "text" : "PENICILLIN G", + "coding" : [ + { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "7980", + "display" : "PENICILLIN G" + }, + { + "system" : "http://fdasis.nlm.nih.gov", + "code" : "Q42T66VG0C", + "display" : "PENICILLIN G" + } + ] + }, + "reaction" : [ + { + "certainty" : "confirmed", + "onset" : "2012-11-07T00:00:00Z", + "manifestation" : [ + { + "text" : "Hives" + } + ] + } + ] +} diff --git a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_30.json b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_30.json index b163ced15..be78790a9 100644 --- a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_30.json +++ b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_30.json @@ -1,22 +1,48 @@ -{"resourceType": "AllergyIntolerance", - "id": "TBwnNbrAqC0Qw5Ha7AFT-2AB", - "clinicalStatus": "active", - "verificationStatus": "confirmed", - "criticality": "low", - "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7980", - "display": "PENICILLIN G"}, - {"system": "http://fdasis.nlm.nih.gov", - "code": "Q42T66VG0C", - "display": "PENICILLIN G"}], - "text": "PENICILLIN G"}, - "patient": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", - "display": "Jason Argonaut"}, - "onsetDateTime": "2012-11-07T00:00:00", - "assertedDate": "2015-08-25T02:11:36", - "recorder": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", - "display": "MOORE, NICK"}, - "reaction": [{"extension": [{"url": "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", - "valueString": "confirmed"}], - "manifestation": [{"text": "Hives"}], - "onset": "2012-11-07T00:00:00"}]} +{ + "resourceType" : "AllergyIntolerance", + "id" : "TBwnNbrAqC0Qw5Ha7AFT-2AB", + "clinicalStatus" : "active", + "verificationStatus" : "confirmed", + "criticality" : "low", + "code" : { + "coding" : [ + { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "7980", + "display" : "PENICILLIN G" + }, + { + "system" : "http://fdasis.nlm.nih.gov", + "code" : "Q42T66VG0C", + "display" : "PENICILLIN G" + } + ], + "text" : "PENICILLIN G" + }, + "patient" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", + "display" : "Jason Argonaut" + }, + "onsetDateTime" : "2012-11-07T00:00:00Z", + "assertedDate" : "2015-08-25T02:11:36", + "recorder" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", + "display" : "MOORE, NICK" + }, + "reaction" : [ + { + "extension" : [ + { + "url" : "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", + "valueString" : "confirmed" + } + ], + "manifestation" : [ + { + "text" : "Hives" + } + ], + "onset" : "2012-11-07T00:00:00Z" + } + ] +} diff --git a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_40.json b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_40.json index 74030f0db..e02782189 100644 --- a/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_40.json +++ b/org.hl7.fhir.convertors/src/test/resources/0_allergy_intolerance_40.json @@ -1,23 +1,61 @@ -{"resourceType": "AllergyIntolerance", - "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "confirmed"}]}, - "verificationStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "confirmed"}]}, - "criticality": "low", - "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "7980", - "display": "PENICILLIN G"}, - {"system": "http://fdasis.nlm.nih.gov", - "code": "Q42T66VG0C", - "display": "PENICILLIN G"}], - "text": "PENICILLIN G"}, - "patient": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", - "display": "Jason Argonaut"}, - "onsetDateTime": "2012-11-07T00:00:00Z", - "recordedDate": "2015-08-25T02:11:36", - "recorder": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", - "display": "MOORE, NICK"}, - "reaction": [{"extension": [{"url": "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", - "valueString": "confirmed"}], - "manifestation": [{"text": "Hives"}], - "onset": "2012-11-07T00:00:00"}]} \ No newline at end of file +{ + "resourceType" : "AllergyIntolerance", + "clinicalStatus" : { + "coding" : [ + { + "system" : "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code" : "confirmed" + } + ] + }, + "verificationStatus" : { + "coding" : [ + { + "system" : "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code" : "confirmed" + } + ] + }, + "criticality" : "low", + "code" : { + "coding" : [ + { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "7980", + "display" : "PENICILLIN G" + }, + { + "system" : "http://fdasis.nlm.nih.gov", + "code" : "Q42T66VG0C", + "display" : "PENICILLIN G" + } + ], + "text" : "PENICILLIN G" + }, + "patient" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", + "display" : "Jason Argonaut" + }, + "onsetDateTime" : "2012-11-07T00:00:00Z", + "recordedDate" : "2015-08-25T02:11:36", + "recorder" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", + "display" : "MOORE, NICK" + }, + "reaction" : [ + { + "extension" : [ + { + "url" : "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", + "valueString" : "confirmed" + } + ], + "manifestation" : [ + { + "text" : "Hives" + } + ], + "onset" : "2012-11-07T00:00:00Z" + } + ] +} diff --git a/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_30.json b/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_30.json index 36324bff6..0e8ebd1fe 100644 --- a/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_30.json +++ b/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_30.json @@ -1,22 +1,48 @@ -{"resourceType": "AllergyIntolerance", - "id": "TKebKfLXzu6Sp.LY-IpvpmQB", - "clinicalStatus": "active", - "verificationStatus": "confirmed", - "criticality": "high", - "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "892484", - "display": "STRAWBERRY"}, - {"system": "http://fdasis.nlm.nih.gov", - "code": "4J2TY8Y81V", - "display": "STRAWBERRY"}], - "text": "STRAWBERRY"}, - "patient": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", - "display": "Jason Argonaut"}, - "onsetDateTime": "2014-03-07T00:00:00", - "assertedDate": "2015-11-07T22:56:34", - "recorder": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", - "display": "MOORE, NICK"}, - "reaction": [{"extension": [{"url": "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", - "valueString": "confirmed"}], - "manifestation": [{"text": "Anaphylaxis"}], - "onset": "2014-03-07T00:00:00"}]} \ No newline at end of file +{ + "resourceType" : "AllergyIntolerance", + "id" : "TKebKfLXzu6Sp.LY-IpvpmQB", + "clinicalStatus" : "active", + "verificationStatus" : "confirmed", + "criticality" : "high", + "code" : { + "coding" : [ + { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "892484", + "display" : "STRAWBERRY" + }, + { + "system" : "http://fdasis.nlm.nih.gov", + "code" : "4J2TY8Y81V", + "display" : "STRAWBERRY" + } + ], + "text" : "STRAWBERRY" + }, + "patient" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", + "display" : "Jason Argonaut" + }, + "onsetDateTime" : "2014-03-07T00:00:00Z", + "assertedDate" : "2015-11-07T22:56:34", + "recorder" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", + "display" : "MOORE, NICK" + }, + "reaction" : [ + { + "extension" : [ + { + "url" : "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", + "valueString" : "confirmed" + } + ], + "manifestation" : [ + { + "text" : "Anaphylaxis" + } + ], + "onset" : "2014-03-07T00:00:00Z" + } + ] +} diff --git a/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_40.json b/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_40.json index cbb724f7d..a38da25a8 100644 --- a/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_40.json +++ b/org.hl7.fhir.convertors/src/test/resources/1_allergy_intolerance_40.json @@ -1,23 +1,61 @@ -{"resourceType": "AllergyIntolerance", - "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", - "code": "confirmed"}]}, - "verificationStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", - "code": "confirmed"}]}, - "criticality": "high", - "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", - "code": "892484", - "display": "STRAWBERRY"}, - {"system": "http://fdasis.nlm.nih.gov", - "code": "4J2TY8Y81V", - "display": "STRAWBERRY"}], - "text": "STRAWBERRY"}, - "patient": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", - "display": "Jason Argonaut"}, - "onsetDateTime": "2014-03-07T00:00:00Z", - "recordedDate": "2015-11-07T22:56:34", - "recorder": {"reference": "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", - "display": "MOORE, NICK"}, - "reaction": [{"extension": [{"url": "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", - "valueString": "confirmed"}], - "manifestation": [{"text": "Anaphylaxis"}], - "onset": "2014-03-07T00:00:00"}]} +{ + "resourceType" : "AllergyIntolerance", + "clinicalStatus" : { + "coding" : [ + { + "system" : "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code" : "confirmed" + } + ] + }, + "verificationStatus" : { + "coding" : [ + { + "system" : "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification", + "code" : "confirmed" + } + ] + }, + "criticality" : "high", + "code" : { + "coding" : [ + { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "892484", + "display" : "STRAWBERRY" + }, + { + "system" : "http://fdasis.nlm.nih.gov", + "code" : "4J2TY8Y81V", + "display" : "STRAWBERRY" + } + ], + "text" : "STRAWBERRY" + }, + "patient" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Patient/Tbt3KuCY0B5PSrJvCu2j-PlK.aiHsu2xUjUM8bWpetXoB", + "display" : "Jason Argonaut" + }, + "onsetDateTime" : "2014-03-07T00:00:00Z", + "recordedDate" : "2015-11-07T22:56:34", + "recorder" : { + "reference" : "https://open-ic.epic.com/Argonaut/api/FHIR/DSTU2/Practitioner/TItWfhjChtlo0pFh9nzctSQB", + "display" : "MOORE, NICK" + }, + "reaction" : [ + { + "extension" : [ + { + "url" : "http://hl7.org/fhir/AllergyIntolerance-r2-certainty", + "valueString" : "confirmed" + } + ], + "manifestation" : [ + { + "text" : "Anaphylaxis" + } + ], + "onset" : "2014-03-07T00:00:00Z" + } + ] +} From 89b0e0a32bb439741f73f3dfb35ec186f508b216 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 1 Sep 2020 09:43:21 +1000 Subject: [PATCH 7/8] Fix NPE in validator and add more validation for bad references --- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../src/main/resources/Messages.properties | 1 + .../instance/InstanceValidator.java | 22 ++++++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 4e4fbecce..d258606dc 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -331,6 +331,7 @@ public class I18nConstants { public static final String REFERENCE_REF_NOTFOUND_BUNDLE = "Reference_REF_NotFound_Bundle"; public static final String REFERENCE_REF_NOTYPE = "Reference_REF_NoType"; public static final String REFERENCE_REF_RESOURCETYPE = "Reference_REF_ResourceType"; + public static final String REFERENCE_REF_SUSPICIOUS = "REFERENCE_REF_SUSPICIOUS"; public static final String REFERENCE_REF_WRONGTARGET = "Reference_REF_WrongTarget"; public static final String REFERENCE_TO__CANNOT_BE_RESOLVED = "reference_to__cannot_be_resolved"; public static final String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 481342e10..a72555b61 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -604,3 +604,4 @@ FHIRPATH_BAD_DATE = Unable to parse Date {0} FHIRPATH_NUMERICAL_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on integer, decimal or Quantity but found {1} FHIRPATH_DECIMAL_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on a decimal but found {1} FHIRPATH_FOCUS_PLURAL = Error evaluating FHIRPath expression: focus for {0} has more than one value +REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect, and it should be checked 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 48179bc5e..517b06b11 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 @@ -2398,6 +2398,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // special known URLs that can't be validated but are known to be valid return; } + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref); ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); String refType; @@ -2439,10 +2440,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } String ft; - if (we != null) + if (we != null) { ft = we.getType(); - else + } else { ft = tryParse(ref); + } if (reference.hasType()) { // R4 onwards... // the type has to match the specified @@ -2596,6 +2598,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + private boolean isSuspiciousReference(String url) { + if (!assumeValidRestReferences || url == null || Utilities.isAbsoluteUrl(url)) { + return false; + } + String[] parts = url.split("\\/"); + if (parts.length == 2 && context.getResourceNames().contains(parts[0]) && Utilities.isValidId(parts[1])) { + return false; + } + if (parts.length == 4 && context.getResourceNames().contains(parts[0]) && Utilities.isValidId(parts[1]) && "_history".equals(parts[2]) && Utilities.isValidId(parts[3])) { + return false; + } + return true; + } + private String asListByUrl(Collection list) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (StructureDefinition sd : list) { @@ -4842,7 +4858,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat case 2: return checkResourceType(parts[0]); default: - if (parts[parts.length - 2].equals("_history")) + if (parts[parts.length - 2].equals("_history") && parts.length >= 4) return checkResourceType(parts[parts.length - 4]); else return checkResourceType(parts[parts.length - 2]); From 39f13c5d65b05717334a587f3468551984fa8574 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 1 Sep 2020 09:54:39 +1000 Subject: [PATCH 8/8] release notes --- RELEASE_NOTES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..c1786be61 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1,14 @@ +Validator: +* Better validation of bad references (and fix NPE) +* Rework output to be more informative and cleaner +* Fix bugs in validation of nested bundles +* Fix bug loading package with no specified version +* fix bugs loading discovered packages on the fly +* Validator now supports logical models in XML wkith no namespaces + +Other code changes: +* Add version conversion for AllergyIntolerance (1.2 <-> 3.0 & 1.2 <-> 4.0) +* Add version conversion for MedicationRequest +* Fix rendering of documents +* fix for not rendering null values in patterns for must-support view +