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 d8c7ec3a8..172f2d687 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 @@ -54,6 +54,7 @@ import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; @@ -172,6 +173,22 @@ import org.hl7.fhir.utilities.xml.SchematronWriter.Section; */ public class ProfileUtilities extends TranslatingUtilities { + public static class SourcedChildDefinitions { + private StructureDefinition source; + private List list; + public SourcedChildDefinitions(StructureDefinition source, List list) { + super(); + this.source = source; + this.list = list; + } + public StructureDefinition getSource() { + return source; + } + public List getList() { + return list; + } + } + public class ElementDefinitionResolution { private StructureDefinition source; @@ -355,7 +372,7 @@ public class ProfileUtilities extends TranslatingUtilities { private XVerExtensionManager xver; private boolean wantFixDifferentialFirstElementType; private Set masterSourceFileNames; - private Map> childMapCache = new HashMap<>(); + private Map childMapCache = new HashMap<>(); private List keyRows = new ArrayList<>(); public ProfileUtilities(IWorkerContext context, List messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) { @@ -433,10 +450,11 @@ public class ProfileUtilities extends TranslatingUtilities { String getLinkForUrl(String corePath, String s); } - public List getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { - if (childMapCache .containsKey(element)) { + public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { + if (childMapCache.containsKey(element)) { return childMapCache.get(element); } + StructureDefinition src = profile; if (element.getContentReference() != null) { List list = null; String id = null; @@ -451,6 +469,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (sd == null) { throw new DefinitionException("unable to process contentReference '"+element.getContentReference()+"' on element '"+element.getId()+"'"); } + src = sd; list = sd.getSnapshot().getElement(); id = ref.substring(ref.indexOf("#")+1); } else { @@ -476,8 +495,9 @@ public class ProfileUtilities extends TranslatingUtilities { } else break; } - childMapCache.put(element, res); - return res; + SourcedChildDefinitions result = new SourcedChildDefinitions(src, res); + childMapCache.put(element, result); + return result; } } @@ -4229,7 +4249,10 @@ public class ProfileUtilities extends TranslatingUtilities { row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); } else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) { row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Element", "BackboneElement")) { + } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { + row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); + keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); + } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); } else { row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); @@ -4877,6 +4900,19 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(piece); c.getPieces().add(gen.new Piece(null, " is prefixed to the value before validation", null)); } + if (definition.hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) { + String ide = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_ID_EXPECTATION); + if (ide.equals("optional")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "Id may or not be present (this is the default for elements but not resources)", null)); + } else if (ide.equals("required")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "Id is required to be present (this is the default for resources but not elements)", null)); + } else if (ide.equals("required")) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "An ID is not allowed in this context", null)); + } + } if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { @@ -4894,6 +4930,24 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); } + if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); + if ("present".equals(code)) { + c.getPieces().add(gen.new Piece(null, "This element is present as a JSON Array even when there are no items in the instance", null)); + } else { + c.getPieces().add(gen.new Piece(null, "This element may be present as a JSON Array even when there are no items in the instance", null)); + } + } + if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, "This object can be represented as null in the JSON structure (which counts as 'present' for cardinality purposes)", null)); + } + if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); + c.getPieces().add(gen.new Piece(null, "Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null)); + } if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } @@ -6268,8 +6322,8 @@ public class ProfileUtilities extends TranslatingUtilities { private org.hl7.fhir.r5.elementmodel.Element generateExample(StructureDefinition profile, ExampleValueAccessor accessor) throws FHIRException { ElementDefinition ed = profile.getSnapshot().getElementFirstRep(); org.hl7.fhir.r5.elementmodel.Element r = new org.hl7.fhir.r5.elementmodel.Element(ed.getPath(), new Property(context, ed, profile)); - List children = getChildMap(profile, ed); - for (ElementDefinition child : children) { + SourcedChildDefinitions children = getChildMap(profile, ed); + for (ElementDefinition child : children.getList()) { if (child.getPath().endsWith(".id")) { org.hl7.fhir.r5.elementmodel.Element id = new org.hl7.fhir.r5.elementmodel.Element("id", new Property(context, child, profile)); id.setValue(profile.getId()+accessor.getId()); @@ -6290,8 +6344,8 @@ public class ProfileUtilities extends TranslatingUtilities { } else { org.hl7.fhir.r5.elementmodel.Element res = new org.hl7.fhir.r5.elementmodel.Element(tail(ed.getPath()), new Property(context, ed, profile)); boolean hasValue = false; - List children = getChildMap(profile, ed); - for (ElementDefinition child : children) { + SourcedChildDefinitions children = getChildMap(profile, ed); + for (ElementDefinition child : children.getList()) { if (!child.hasContentReference()) { org.hl7.fhir.r5.elementmodel.Element e = createExampleElement(profile, child, accessor); if (e != null) { 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 3d08c8012..1217eb405 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 @@ -1,5 +1,7 @@ package org.hl7.fhir.r5.elementmodel; +import java.io.PrintStream; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -75,7 +77,7 @@ public class Element extends Base { public enum SpecialElement { - CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER; + CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER, LOGICAL; public static SpecialElement fromProperty(Property property) { if (property.getStructure().getType().equals("Parameters")) @@ -87,7 +89,7 @@ public class Element extends Base { if (property.getName().equals("contained")) return CONTAINED; if (property.getStructure().getKind() == StructureDefinitionKind.LOGICAL) - return CONTAINED; + return LOGICAL; throw new FHIRException("Unknown resource containing a native resource: "+property.getDefinition().getId()); } @@ -97,6 +99,7 @@ public class Element extends Base { case BUNDLE_OUTCOME: return "outcome"; case CONTAINED: return "contained"; case PARAMETER: return "parameter"; + case LOGICAL: return "logical"; default: return "??"; } } @@ -124,6 +127,7 @@ public class Element extends Base { private Map> childMap; private int descendentCount; private int instanceId; + private boolean isNull; public Element(String name) { super(); @@ -211,7 +215,15 @@ public class Element extends Base { } - public boolean hasValue() { + public boolean isNull() { + return isNull; + } + + public void setNull(boolean isNull) { + this.isNull = isNull; + } + + public boolean hasValue() { return value != null; } @@ -1127,6 +1139,65 @@ public class Element extends Base { public void setInstanceId(int instanceId) { this.instanceId = instanceId; } - + + public void printToOutput() { + printToOutput(System.out, ""); + + } + + private void printToOutput(PrintStream out, String indent) { + String s = indent+name +(index == -1 ? "" : "["+index+"]") +(special != null ? "$"+special.toHuman(): "")+ (type!= null || explicitType != null ? " : "+type+(explicitType != null ? "/'"+explicitType+"'" : "") : ""); + if (isNull) { + s = s + " = (null)"; + } else if (value != null) { + s = s + " = '"+value+"'"; + } else if (xhtml != null) { + s = s + " = (xhtml)"; + } + if (property != null) { + s = s +" {"+property.summary(); + if (elementProperty != null) { + s = s +" -> "+elementProperty.summary(); + } + s = s + "}"; + } + if (line > 0) { + s = s + " (l"+line+":c"+col+")"; + } + out.println(s); + if (children != null) { + for (Element child : children) { + child.printToOutput(out, indent+" "); + } + } + + } + + private String msgCounts() { + int e = 0; + int w = 0; + int h = 0; + for (ValidationMessage msg : messages) { + switch (msg.getLevel()) { + case ERROR: + e++; + break; + case FATAL: + e++; + break; + case INFORMATION: + h++; + break; + case NULL: + break; + case WARNING: + w++; + break; + default: + break; + } + } + return "e:"+e+",w:"+w+",h:"+h; + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java index a24952ec5..60a364a44 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/JsonParser.java @@ -57,6 +57,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.StringPair; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -240,7 +241,11 @@ public class JsonParser extends ParserBase { if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) { JsonArray arr = (JsonArray) e; if (arr.size() == 0) { - logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR); + if (property.canBeEmpty()) { + // nothing + } else { + logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR); + } } int c = 0; for (JsonElement am : arr) { @@ -288,7 +293,7 @@ public class JsonParser extends ParserBase { String fpathV = fpathArr+"."+propV.getName(); if (propV.isPrimitive(propV.getType(null))) { parseChildPrimitiveInstance(n, propV, propV.getName(), npathV, fpathV, pv.getValue(), null); - } else if (pv.getValue() instanceof JsonObject) { + } else if (pv.getValue() instanceof JsonObject || pv.getValue() instanceof JsonNull) { parseChildComplexInstance(npathV, fpathV, n, propV, propV.getName(), pv.getValue()); } else { logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(pv.getValue())), IssueSeverity.ERROR); @@ -354,12 +359,22 @@ public class JsonParser extends ParserBase { n.setPath(fpath); checkObject(child, npath); element.getChildren().add(n); - if (property.isResource()) + if (property.isResource()) { parseResource(npath, child, n, property); - else + } else { parseChildren(npath, child, n, false); - } else + } + } else if (property.isNullable() && e instanceof JsonNull) { + // we create an element marked as a null element so we know something was present + JsonNull child = (JsonNull) e; + Element n = new Element(name, property).markLocation(line(child), col(child)); + n.setPath(fpath); + element.getChildren().add(n); + n.setNull(true); + // nothing to do, it's ok, but we treat it like it doesn't exist + } else { logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE__NOT_, (property.isList() ? "an Array" : "an Object"), describe(e), name, npath), IssueSeverity.ERROR); + } } private String describe(JsonElement e) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java index 4fbc7c8db..18606b90a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java @@ -37,6 +37,7 @@ import java.util.List; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement; import org.hl7.fhir.r5.formats.IParser.OutputStyle; @@ -93,8 +94,8 @@ public class ObjectConverter { if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) res.setValue(((PrimitiveType) base).asStringValue()); - List children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); - for (ElementDefinition child : children) { + SourcedChildDefinitions children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); + for (ElementDefinition child : children.getList()) { String n = tail(child.getPath()); if (sd.getKind() != StructureDefinitionKind.PRIMITIVETYPE || !"value".equals(n)) { Base[] values = base.getProperty(n.hashCode(), n, false); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java index c31408e26..8011b3f85 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java @@ -39,6 +39,7 @@ import java.util.Map; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.formats.FormatUtilities; import org.hl7.fhir.r5.model.ElementDefinition; @@ -229,8 +230,10 @@ public class Property { } public boolean isResource() { - if (definition.getType().size() > 0) - return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode())); + if (definition.getType().size() > 0) { + String tc = definition.getType().get(0).getCode(); + return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames())); + } else return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); } @@ -300,9 +303,9 @@ public class Property { protected List getChildProperties(String elementName, String statedType) throws FHIRException { ElementDefinition ed = definition; StructureDefinition sd = structure; - List children = profileUtilities.getChildMap(sd, ed); + SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); String url = null; - if (children.isEmpty() || isElementWithOnlyExtension(ed, children)) { + if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) { // ok, find the right definitions String t = null; if (ed.getType().size() == 1) @@ -369,7 +372,7 @@ public class Property { } } List properties = new ArrayList(); - for (ElementDefinition child : children) { + for (ElementDefinition child : children.getList()) { properties.add(new Property(context, child, sd, this.profileUtilities)); } return properties; @@ -378,8 +381,8 @@ public class Property { protected List getChildProperties(TypeDetails type) throws DefinitionException { ElementDefinition ed = definition; StructureDefinition sd = structure; - List children = profileUtilities.getChildMap(sd, ed); - if (children.isEmpty()) { + SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed); + if (children.getList().isEmpty()) { // ok, find the right definitions String t = null; if (ed.getType().size() == 1) @@ -408,7 +411,7 @@ public class Property { } } List properties = new ArrayList(); - for (ElementDefinition child : children) { + for (ElementDefinition child : children.getList()) { properties.add(new Property(context, child, sd, this.profileUtilities)); } return properties; @@ -510,4 +513,23 @@ public class Property { } + public boolean isNullable() { + return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE); + } + + + public String summary() { + return structure.getUrl()+"#"+definition.getId(); + } + + + public boolean canBeEmpty() { + if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { + return !"absent".equals(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY)); + } else { + return false; + } + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index e1d39beec..0cee3a17b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -1109,8 +1109,19 @@ public class DataRenderer extends Renderer { s = "id: "+s; } - if (ii.hasUse()) - s = s + " ("+ii.getUse().toString()+")"; + if (ii.hasUse() || ii.hasPeriod()) { + s = s + "("; + if (ii.hasUse()) { + s = s + "use: "+ii.getUse().toString(); + } + if (ii.hasUse() && ii.hasPeriod()) { + s = s + ", "; + } + if (ii.hasPeriod()) { + s = s + "period: "+displayPeriod(ii.getPeriod()); + } + s = s + ")"; + } return s; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 78d987b4b..23c59c21d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -27,6 +27,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; @@ -5767,10 +5768,10 @@ public class FHIRPathEngine { if (expr.getName().equals("$this")) { focus = element; } else { - List childDefinitions; + SourcedChildDefinitions childDefinitions; childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); // if that's empty, get the children of the type - if (childDefinitions.isEmpty()) { + if (childDefinitions.getList().isEmpty()) { sd = fetchStructureByType(element, expr); if (sd == null) { @@ -5778,7 +5779,7 @@ public class FHIRPathEngine { } childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); } - for (ElementDefinition t : childDefinitions) { + for (ElementDefinition t : childDefinitions.getList()) { if (tailMatches(t, expr.getName()) && !t.hasSlicing()) { // GG: slicing is a problem here. This is for an exetnsion with a fixed value (type slicing) focus = new TypedElementDefinition(t); break; @@ -5806,8 +5807,8 @@ public class FHIRPathEngine { focus = new TypedElementDefinition(sd.getSnapshot().getElementFirstRep()); } else if ("extension".equals(expr.getName())) { String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue(); - List childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); - for (ElementDefinition t : childDefinitions) { + SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); + for (ElementDefinition t : childDefinitions.getList()) { if (t.getPath().endsWith(".extension") && t.hasSliceName()) { System.out.println("t: "+t.getId()); StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ? @@ -5816,7 +5817,7 @@ public class FHIRPathEngine { exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition()); } if (exsd != null && exsd.getUrl().equals(targetUrl)) { - if (profileUtilities.getChildMap(sd, t).isEmpty()) { + if (profileUtilities.getChildMap(sd, t).getList().isEmpty()) { sd = exsd; } focus = new TypedElementDefinition(t); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index c386925e3..71de59075 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -216,9 +216,11 @@ public class ToolingExtensions { public static final String EXT_BINDING_ADDITIONAL = "http://hl7.org/fhir/tools/StructureDefinition/additional-binding"; public static final String EXT_JSON_PROP_KEY = "http://hl7.org/fhir/tools/StructureDefinition/json-property-key"; public static final String EXT_JSON_EMPTY = "http://hl7.org/fhir/tools/StructureDefinition/json-empty-behavior"; + public static final String EXT_JSON_NULLABLE = "http://hl7.org/fhir/tools/StructureDefinition/json-nullable"; public static final String EXT_IMPLIED_PREFIX = "http://hl7.org/fhir/tools/StructureDefinition/implied-string-prefix"; public static final String EXT_DATE_FORMAT = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-date-format"; - + public static final String EXT_ID_EXPECTATION = "http://hl7.org/fhir/tools/StructureDefinition/id-expectation"; + // unregistered? - don't know what these are used for public static final String EXT_MAPPING_PREFIX = "http://hl7.org/fhir/tools/StructureDefinition/logical-mapping-prefix"; @@ -230,7 +232,7 @@ public class ToolingExtensions { public static final String EXT_MAPPING_CARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-source-cardinality"; public static final String EXT_MAPPING_TGTTYPE = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-type"; public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality"; - + // specific extension helpers public static Extension makeIssueSource(Source source) { @@ -482,6 +484,8 @@ public class ToolingExtensions { return false; if (!(ex.getValue() instanceof BooleanType)) return false; + if (!(ex.getValue().hasPrimitiveValue())) + return false; return ((BooleanType) ex.getValue()).getValue(); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java index 46ce3505c..0933e93e0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java @@ -2516,7 +2516,7 @@ public class StructureMapUtilities { private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException { boolean first = true; - List children = profileUtilities.getChildMap(sd, ed); + List children = profileUtilities.getChildMap(sd, ed).getList(); for (ElementDefinition child : children) { if (first && inner) { b.append(" then {\r\n"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java index 84004111d..c5e86fee2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java @@ -1,7 +1,27 @@ package org.hl7.fhir.r5.utils.validation.constants; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.utilities.Utilities; + public enum IdStatus { OPTIONAL, REQUIRED, - PROHIBITED + PROHIBITED; + + public static IdStatus fromCode(String v) { + if (v == null || Utilities.noString(v)) { + return null; + } else { + v = v.toLowerCase(); + if (v.equals("optional")) { + return OPTIONAL; + } else if (v.equals("required")) { + return REQUIRED; + } else if (v.equals("prohibited")) { + return PROHIBITED; + } else { + throw new FHIRException("Unkonwn Id Status code '"+v+"'"); + } + } + } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index 8b04ba54a..3f9fae531 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -304,7 +304,7 @@ public class VersionUtilities { public static boolean isMajMinOrLaterPatch(String test, String current) { String t = getMajMin(test); String c = getMajMin(current); - if (c != null && c.compareTo(t) == 0) { + if (c != null && t != null && c.compareTo(t) == 0) { String pt = getPatch(test); String pc = getPatch(current); if (pt==null || "x".equals(pt)) { 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 43849de91..40f09ff78 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 @@ -80,6 +80,7 @@ public class I18nConstants { public static final String DUPLICATE_ID = "DUPLICATE_ID"; public static final String DUPLICATE_RESOURCE_ = "Duplicate_Resource_"; public static final String DUPLICATE_RESOURCE_VERSION = "DUPLICATE_RESOURCE_VERSION"; + public static final String ELEMENT_CANNOT_BE_NULL = "ELEMENT_CANNOT_BE_NULL"; public static final String ELEMENT_ID__NULL__ON_ = "element_id__null__on_"; public static final String ELEMENT_MUST_HAVE_SOME_CONTENT = "Element_must_have_some_content"; public static final String ELEMENT__NULL_ = "element__null_"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java index 84fc35ba4..8c0e6d7fd 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java @@ -437,8 +437,10 @@ public class JsonTrackingParser { else throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString()); - parseProperty(); - readObject(result, true); + if (lexer.getType() != TokenType.Close) { + parseProperty(); + readObject(result, true); + } if (map != null) map.put(result, loc); return result; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 281f2e177..dfcda669e 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -748,5 +748,6 @@ TYPE_SPECIFIC_CHECKS_DT_MARKDOWN_HTML = The markdown contains content that appea TYPE_SPECIFIER_ILLEGAL_TYPE = The Type specifier {1} specified an illegal type {0} TYPE_SPECIFIER_ABSTRACT_TYPE = The Type specifier {1} specified an abstract type {0} TYPE_SPECIFIER_NM_ILLEGAL_TYPE = No Type specifier matched, and the underlying type {0} is not valid -TYPE_SPECIFIER_NM_ABSTRACT_TYPE = = No Type specifier matched, and the underlying type {0} is not abstract +TYPE_SPECIFIER_NM_ABSTRACT_TYPE = No Type specifier matched, and the underlying type {0} is not abstract +ELEMENT_CANNOT_BE_NULL = The element is not allowed to be 'null' } 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 1752e999b..c0122700e 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 @@ -62,6 +62,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; @@ -249,6 +250,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private static final String EXECUTION_ID = "validator.execution.id"; private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)"; private static final boolean STACK_TRACE = false; + private static final boolean DEBUG_ELEMENT = false; private class ValidatorHostServices implements IEvaluationContext { @@ -863,6 +865,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } errors.removeAll(messagesToRemove); timeTracker.overall(t); + if (DEBUG_ELEMENT) { + element.printToOutput(); + } } @@ -3622,8 +3627,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); timeTracker.sd(t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) { return sd; + } + if (sd.getAbstract()) { + StructureDefinition sdt = context.fetchTypeDefinition(type); + StructureDefinition tt = sdt; + while (tt != null) { + if (tt.getBaseDefinition().equals(sd.getUrl())) { + return sdt; + } + } + + + } } return null; } @@ -4854,49 +4871,56 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct) throws FHIRException { - SpecialElement special = element.getSpecial(); - - ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? - ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, - hostContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl()); - - if (containedValidationPolicy.ignore()) { - return; - } - - String resourceName = element.getType(); - TypeRefComponent typeForResource = null; - CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder(); - - // Iterate through all possible types - for (TypeRefComponent type : child.getType()) { - bt.append(type.getCode()); - if (type.getCode().equals("Resource") || type.getCode().equals(resourceName) ) { - typeForResource = type; - break; + if (element.isNull()) { + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), ToolingExtensions.readBooleanExtension(child, ToolingExtensions.EXT_JSON_NULLABLE), + I18nConstants.ELEMENT_CANNOT_BE_NULL)) { + // nothing else to validate? } - } - stack.qualifyPath(".ofType("+resourceName+")"); + } else { + SpecialElement special = element.getSpecial(); - if (typeForResource == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), - false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); - } else if (isValidResourceType(resourceName, typeForResource)) { - if (containedValidationPolicy.checkValid()) { - // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise - ValidatorHostContext hc = null; - if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.PARAMETER) { - resource = element; - assert Utilities.existsInList(hostContext.getRootResource().fhirType(), "Bundle", "Parameters") : "Resource is "+hostContext.getRootResource().fhirType()+", expected Bundle or Parameters"; - hc = hostContext.forEntry(element, hostContext.getRootResource()); // root becomes the grouping resource (should be either bundle or parameters) - } else { - hc = hostContext.forContained(element); + ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? + ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, + hostContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl()); + + if (containedValidationPolicy.ignore()) { + return; + } + + String resourceName = element.getType(); + TypeRefComponent typeForResource = null; + CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder(); + + // Iterate through all possible types + for (TypeRefComponent type : child.getType()) { + bt.append(type.getCode()); + if (type.getCode().equals("Resource") || type.getCode().equals(resourceName) ) { + typeForResource = type; + break; } + } - stack.resetIds(); - if (special != null) { - switch (special) { + stack.qualifyPath(".ofType("+resourceName+")"); + + if (typeForResource == null) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); + } else if (isValidResourceType(resourceName, typeForResource)) { + if (containedValidationPolicy.checkValid()) { + // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise + ValidatorHostContext hc = null; + if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.PARAMETER) { + resource = element; + assert Utilities.existsInList(hostContext.getResource().fhirType(), "Bundle", "Parameters") : "Containing Resource is "+hostContext.getResource().fhirType()+", expected Bundle or Parameters at "+stack.getLiteralPath(); + hc = hostContext.forEntry(element, hostContext.getResource()); // root becomes the grouping resource (should be either bundle or parameters) + } else { + hc = hostContext.forContained(element); + } + + stack.resetIds(); + if (special != null) { + switch (special) { case BUNDLE_ENTRY: case BUNDLE_OUTCOME: case PARAMETER: @@ -4908,51 +4932,52 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat break; default: break; + } } - } - if (typeForResource.getProfile().size() == 1) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue()); - timeTracker.sd(t); - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), - profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) { - validateResource(hc, errors, resource, element, profile, idstatus, stack, pct); + if (typeForResource.getProfile().size() == 1) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue()); + timeTracker.sd(t); + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) { + validateResource(hc, errors, resource, element, profile, idstatus, stack, pct); + } + } else if (typeForResource.getProfile().isEmpty()) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, + "http://hl7.org/fhir/StructureDefinition/" + resourceName); + timeTracker.sd(t); + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) { + validateResource(hc, errors, resource, element, profile, idstatus, stack, pct); + } + } else { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (CanonicalType u : typeForResource.getProfile()) { + b.append(u.asStringValue()); + } + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES, special.toHuman(), typeForResource.getCode(), b.toString()); } - } else if (typeForResource.getProfile().isEmpty()) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, - "http://hl7.org/fhir/StructureDefinition/" + resourceName); - timeTracker.sd(t); - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), - profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) { - validateResource(hc, errors, resource, element, profile, idstatus, stack, pct); - } - } else { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (CanonicalType u : typeForResource.getProfile()) { - b.append(u.asStringValue()); - } - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), - false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES, special.toHuman(), typeForResource.getCode(), b.toString()); } - } - } else { - List types = new ArrayList<>(); - for (UriType u : typeForResource.getProfile()) { - StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); - if (sd != null && !types.contains(sd.getType())) { - types.add(sd.getType()); - } - } - if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), - false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE2, resourceName, types.get(0)); } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), - false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); + List types = new ArrayList<>(); + for (UriType u : typeForResource.getProfile()) { + StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); + if (sd != null && !types.contains(sd.getType())) { + types.add(sd.getType()); + } + } + if (types.size() == 1) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE2, resourceName, types.get(0)); + } else { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); + } } } } @@ -5019,8 +5044,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } // get the list of direct defined children, including slices - List childDefinitions = profileUtilities.getChildMap(profile, definition); - if (childDefinitions.isEmpty()) { + SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(profile, definition); + if (childDefinitions.getList().isEmpty()) { if (actualType == null) return; // there'll be an error elsewhere in this case, and we're going to stop. childDefinitions = getActualTypeChildren(hostContext, element, actualType); @@ -5028,7 +5053,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // this only happens when the profile constrains the abstract children but leaves th choice open. if (actualType == null) return; // there'll be an error elsewhere in this case, and we're going to stop. - List typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); + SourcedChildDefinitions typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) childDefinitions = mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); } @@ -5045,27 +5070,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - private List mergeChildLists(List source, List additional, String masterPath, String typePath) { - List res = new ArrayList<>(); - res.addAll(source); - for (ElementDefinition ed : additional) { + private SourcedChildDefinitions mergeChildLists(SourcedChildDefinitions source, SourcedChildDefinitions additional, String masterPath, String typePath) { + SourcedChildDefinitions res = new SourcedChildDefinitions(additional.getSource(), new ArrayList<>()); + res.getList().addAll(source.getList()); + for (ElementDefinition ed : additional.getList()) { boolean inMaster = false; - for (ElementDefinition t : source) { + for (ElementDefinition t : source.getList()) { String tp = masterPath + ed.getPath().substring(typePath.length()); if (t.getPath().equals(tp)) { inMaster = true; } } if (!inMaster) { - res.add(ed); + res.getList().add(ed); } } return res; } // todo: the element definition in context might assign a constrained profile for the type? - public List getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { - List childDefinitions; + public SourcedChildDefinitions getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { + SourcedChildDefinitions childDefinitions; StructureDefinition dt = null; if (isAbsolute(actualType)) dt = this.context.fetchResource(StructureDefinition.class, actualType); @@ -5120,16 +5145,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat && !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) { type = checkDefn.getType().get(0).getWorkingCode(); String stype = ei.getElement().fhirType(); - if (checkDefn.isChoice() && !stype.equals(type)) { - if (extensionUrl != null && !isAbsolute(extensionUrl)) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); - } else if (!isAbstractType(type) && !"Extension".equals(profile.getType())) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type), I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); - } - } else if (!isAbstractType(type)) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type) || + if (!stype.equals(type)) { + if (checkDefn.isChoice()) { + if (extensionUrl != null && !isAbsolute(extensionUrl)) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); + } else if (!isAbstractType(type) && !"Extension".equals(profile.getType())) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type), I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); + } + } else if (!isAbstractType(type)) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), stype.equals(type) || (Utilities.existsInList(type, "string", "id") && Utilities.existsInList(stype, "string", "id")), // work around a r4 problem with id/string - I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); + I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); + } else if (!isResource(type)) { +// System.out.println("update type "+type+" to "+stype+"?"); + type = stype; + } else { + // this will be sorted out in contains ... System.out.println("update type "+type+" to "+stype+"?"); + } } // Excluding reference is a kludge to get around versioning issues @@ -5200,9 +5232,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } NodeStack localStack = stack.push(ei.getElement(), "*".equals(ei.getDefinition().getBase().getMax()) && ei.count == -1 ? 0 : ei.count, checkDefn, type == null ? typeDefn : resolveType(type, checkDefn.getType())); - if (debug) { - System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()+time()); - } + if (debug) { + System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()+time()); + } String localStackLiteralPath = localStack.getLiteralPath(); String eiPath = ei.getPath(); if (!eiPath.equals(localStackLiteralPath)) { @@ -5450,9 +5482,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } public void checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, - List childDefinitions, List children, List problematicPaths) throws DefinitionException { + SourcedChildDefinitions childDefinitions, List children, List problematicPaths) throws DefinitionException { // 3. report any definitions that have a cardinality problem - for (ElementDefinition ed : childDefinitions) { + for (ElementDefinition ed : childDefinitions.getList()) { if (ed.getRepresentation().isEmpty()) { // ignore xml attributes int count = 0; List slices = null; @@ -5490,7 +5522,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } public List assignChildren(ValidatorHostContext hostContext, List errors, StructureDefinition profile, Element resource, - NodeStack stack, List childDefinitions, List children) throws DefinitionException { + NodeStack stack, SourcedChildDefinitions childDefinitions, List children) throws DefinitionException { // 2. assign children to a definition // for each definition, for each child, check whether it belongs in the slice ElementDefinition slicer = null; @@ -5498,8 +5530,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List problematicPaths = new ArrayList(); String slicingPath = null; int sliceOffset = 0; - for (int i = 0; i < childDefinitions.size(); i++) { - ElementDefinition ed = childDefinitions.get(i); + for (int i = 0; i < childDefinitions.getList().size(); i++) { + ElementDefinition ed = childDefinitions.getList().get(i); boolean childUnsupportedSlicing = false; boolean process = true; if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) { @@ -5553,7 +5585,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { // Don't raise this if we're in an abstract profile, like Resource - if (!profile.getAbstract()) { + if (!childDefinitions.getSource().getAbstract()) { rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), I18nConstants.VALIDATION_VAL_PROFILE_NOTALLOWED, profile.getUrl()); } } @@ -5674,7 +5706,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { - if (isBundleEntry(ei.getPath())) { + if (ei.getDefinition().hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) { + return IdStatus.fromCode(ToolingExtensions.readStringExtension(ei.getDefinition(),ToolingExtensions.EXT_ID_EXPECTATION)); + } else if (isBundleEntry(ei.getPath())) { Element req = ep.getNamedChild("request"); Element resp = ep.getNamedChild("response"); Element fullUrl = ep.getNamedChild(FULL_URL); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index 019bb3f17..31b557cde 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -126,8 +126,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe private JsonObject content; private String version; private String name; - private static StringBuilder logB = new StringBuilder(); - + private static Map ve = new HashMap<>(); @@ -145,7 +144,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe CacheVerificationLogger logger = new CacheVerificationLogger(); long setup = System.nanoTime(); - logOutputToFile("---- " + name + " ---------------------------------------------------------------- ("+System.getProperty("java.vm.name")+")"); + logOutput("---- " + name + " ---------------------------------------------------------------- ("+System.getProperty("java.vm.name")+")"); logOutput("** Core: "); String txLog = null; if (content.has("txLog")) { @@ -220,7 +219,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe if (content.has("packages")) { for (JsonElement e : content.getAsJsonArray("packages")) { String n = e.getAsString(); - logOutputToFile("load package "+n); + logOutput("load package "+n); InputStream cnt = n.endsWith(".tgz") ? TestingUtilities.loadTestResourceStream("validator", n) : null; if (cnt != null) { igLoader.loadPackage(NpmPackage.fromPackage(cnt), true); @@ -237,7 +236,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe String filename = e.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); - logOutputToFile("load resource "+mr.getUrl()); + logOutput("load resource "+mr.getUrl()); val.getContext().cacheResource(mr); if (mr instanceof ImplementationGuide) { val.getImplementationGuides().add((ImplementationGuide) mr); @@ -253,7 +252,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe String filename = je.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); StructureDefinition sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext()); - logOutputToFile("load resource "+sd.getUrl()); + logOutput("load resource "+sd.getUrl()); val.getContext().cacheResource(sd); } } @@ -289,7 +288,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe JsonObject profile = content.getAsJsonObject("profile"); if (profile.has("packages")) { for (JsonElement e : profile.getAsJsonArray("packages")) { - logOutputToFile("load package "+e.getAsString()); + logOutput("load package "+e.getAsString()); igLoader.loadIg(vCurr.getIgs(), vCurr.getBinaries(), e.getAsString(), true); } } @@ -301,7 +300,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe String filename = e.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); - logOutputToFile("load resource "+mr.getUrl()); + logOutput("load resource "+mr.getUrl()); val.getContext().cacheResource(mr); if (mr instanceof ImplementationGuide) { val.getImplementationGuides().add((ImplementationGuide) mr); @@ -317,7 +316,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe logOutput("Name: " + name + " - profile : " + profile.get("source").getAsString()); version = content.has("version") ? content.get("version").getAsString() : version; sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext()); - logOutputToFile("load resource "+sd.getUrl()); + logOutput("load resource "+sd.getUrl()); val.getContext().cacheResource(sd); } val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false); @@ -338,13 +337,13 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe if (mr instanceof StructureDefinition) { new ContextUtilities(val.getContext()).generateSnapshot((StructureDefinition) mr, true); } - logOutputToFile("load resource "+mr.getUrl()); + logOutput("load resource "+mr.getUrl()); val.getContext().cacheResource(mr); } } if (logical.has("packages")) { for (JsonElement e : logical.getAsJsonArray("packages")) { - logOutputToFile("load package "+e.getAsString()); + logOutput("load package "+e.getAsString()); igLoader.loadIg(vCurr.getIgs(), vCurr.getBinaries(), e.getAsString(), true); } } @@ -557,13 +556,6 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe System.out.println(msg); } - private void logOutputToFile(String msg) throws IOException { - System.out.println(msg); - logB .append(msg); - logB.append("\r\n"); - TextFile.stringToFile(logB.toString(), Utilities.path("[tmp]", "validation-test-log.txt")); - } - private OperationOutcomeIssueComponent findMatchingIssue(OperationOutcome oo, OperationOutcomeIssueComponent iss) { for (OperationOutcomeIssueComponent t : oo.getIssue()) { if (t.getExpression().get(0).getValue().equals(iss.getExpression().get(0).getValue()) && t.getCode() == iss.getCode() && t.getSeverity() == iss.getSeverity() diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache index 41b8f0e4e..62866de8e 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/all-systems.cache @@ -2039,3 +2039,12 @@ v: { "error" : "The code \"[%payloadFormat%]\" is not valid in the system urn:ietf:bcp:13; The code provided (urn:ietf:bcp:13#[%payloadFormat%]) is not valid in the value set 'Mime Types' (from http://tx.fhir.org/r4)" } ------------------------------------------------------------------------------------- +{"code" : { + "code" : "h" +}, "url": "http://hl7.org/fhir/ValueSet/units-of-time", "version": "4.0.1", "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "小时", + "code" : "h", + "system" : "http://unitsofmeasure.org" +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache index 74e6a6f21..b48c1e8c2 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache @@ -1966,3 +1966,14 @@ v: { "system" : "http://loinc.org" } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://loinc.org", + "code" : "80764-4", + "display" : "Pain medicine Plan of care note" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "Pain medicine Plan of care note", + "code" : "80764-4", + "system" : "http://loinc.org" +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/rxnorm.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/rxnorm.cache index 5499a1902..11156b33c 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/rxnorm.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/rxnorm.cache @@ -9,3 +9,104 @@ v: { "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1010603", + "display" : "Suboxone 2 MG / 0.5 MG Sublingual Film" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film [Suboxone]", + "code" : "1010603", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1049502", + "display" : "12 HR Oxycodone Hydrochloride 10 MG Extended Release Oral Tablet" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "12 HR oxycodone hydrochloride 10 MG Extended Release Oral Tablet", + "code" : "1049502", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1010600", + "display" : "Buprenorphine 2 MG / Naloxone 0.5 MG Oral Strip" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film", + "code" : "1010600", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "severity" : "warning", + "error" : "The display \"Buprenorphine 2 MG / Naloxone 0.5 MG Oral Strip\" is not a valid display for the code {http://www.nlm.nih.gov/research/umls/rxnorm}1010600 - should be one of ['buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film', 'buprenorphine 2 MG / naloxone 0.5 MG Buccal Film', 'buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film', 'buprenorphine HCl 2 MG / naloxone HCl 0.5 MG Sublingual Film'] (from http://tx.fhir.org/r4)" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "197696", + "display" : "72 HR Fentanyl 0.075 MG/HR Transdermal System" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "72 HR fentanyl 0.075 MG/HR Transdermal System", + "code" : "197696", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "892495", + "display" : "Morphine Sulfate 10 MG [Kadian]" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "morphine sulfate 10 MG [Kadian]", + "code" : "892495", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1049502", + "display" : "oxyCODONE HCl 10 MG 12HR Extended Release Oral Tablet" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "12 HR oxycodone hydrochloride 10 MG Extended Release Oral Tablet", + "code" : "1049502", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "836397", + "display" : "Acetaminophen 325 MG / tramadol hydrochloride 37.5 MG Oral Tablet [Ultracet]" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "acetaminophen 325 MG / tramadol hydrochloride 37.5 MG Oral Tablet [Ultracet]", + "code" : "836397", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1298088", + "display" : "Flurazepam Hydrochloride 15 MG Oral Capsule" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "flurazepam hydrochloride 15 MG Oral Capsule", + "code" : "1298088", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm", + "code" : "1010600", + "display" : "buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "buprenorphine 2 MG / naloxone 0.5 MG Sublingual Film", + "code" : "1010600", + "system" : "http://www.nlm.nih.gov/research/umls/rxnorm" +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/snomed.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/snomed.cache index e9f41f336..b11192a32 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/snomed.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/snomed.cache @@ -2002,3 +2002,57 @@ v: { "system" : "http://snomed.info/sct" } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://snomed.info/sct", + "code" : "310627008", + "display" : "Urine drug screening (procedure)" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "Urine drug screening", + "code" : "310627008", + "system" : "http://snomed.info/sct" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://snomed.info/sct", + "code" : "1049502", + "display" : "12 HR Oxycodone Hydrochloride 10 MG Extended Release Oral Tablet" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "severity" : "error", + "error" : "Unable to find code 1049502 in http://snomed.info/sct (version http://snomed.info/sct/900000000000207008/version/20220731); The code \"1049502\" is not valid in the system http://snomed.info/sct; The code provided (http://snomed.info/sct#1049502) is not valid in the value set 'All codes known to the system' (from http://tx.fhir.org/r4)" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://snomed.info/sct", + "code" : "82423001", + "display" : "Chronic pain" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "Chronic pain", + "code" : "82423001", + "system" : "http://snomed.info/sct" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://snomed.info/sct", + "code" : "454281000124100", + "display" : "Assessment of risk for opioid abuse (procedure)" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "severity" : "error", + "error" : "Unable to find code 454281000124100 in http://snomed.info/sct (version http://snomed.info/sct/900000000000207008/version/20220731); The code \"454281000124100\" is not valid in the system http://snomed.info/sct; The code provided (http://snomed.info/sct#454281000124100) is not valid in the value set 'All codes known to the system' (from http://tx.fhir.org/r4)" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://snomed.info/sct", + "version" : "http://snomed.info/sct/731000124108", + "code" : "454281000124100", + "display" : "Assessment of risk for opioid abuse (procedure)" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "Assessment of risk for opioid abuse (procedure)", + "code" : "454281000124100", + "system" : "http://snomed.info/sct" +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/ucum.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/ucum.cache index 746f853e3..5753cd7ac 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/ucum.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/ucum.cache @@ -205,3 +205,23 @@ v: { "error" : "The code provided (http://unitsofmeasure.org#m) is not valid (from http://tx.fhir.org/r4)" } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://unitsofmeasure.org", + "code" : "{patch}" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "{patch}", + "code" : "{patch}", + "system" : "http://unitsofmeasure.org" +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://unitsofmeasure.org", + "code" : "{capsule}" +}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}#### +v: { + "display" : "{capsule}", + "code" : "{capsule}", + "system" : "http://unitsofmeasure.org" +} +-------------------------------------------------------------------------------------