Updates to validator for CDS Hooks support

This commit is contained in:
Grahame Grieve 2022-10-21 16:25:07 +11:00
parent c053f08a13
commit 41950eeb0a
19 changed files with 582 additions and 169 deletions

View File

@ -54,6 +54,7 @@ import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError; 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.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; 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 class ProfileUtilities extends TranslatingUtilities {
public static class SourcedChildDefinitions {
private StructureDefinition source;
private List<ElementDefinition> list;
public SourcedChildDefinitions(StructureDefinition source, List<ElementDefinition> list) {
super();
this.source = source;
this.list = list;
}
public StructureDefinition getSource() {
return source;
}
public List<ElementDefinition> getList() {
return list;
}
}
public class ElementDefinitionResolution { public class ElementDefinitionResolution {
private StructureDefinition source; private StructureDefinition source;
@ -355,7 +372,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private XVerExtensionManager xver; private XVerExtensionManager xver;
private boolean wantFixDifferentialFirstElementType; private boolean wantFixDifferentialFirstElementType;
private Set<String> masterSourceFileNames; private Set<String> masterSourceFileNames;
private Map<ElementDefinition, List<ElementDefinition>> childMapCache = new HashMap<>(); private Map<ElementDefinition, SourcedChildDefinitions> childMapCache = new HashMap<>();
private List<String> keyRows = new ArrayList<>(); private List<String> keyRows = new ArrayList<>();
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) { public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
@ -433,10 +450,11 @@ public class ProfileUtilities extends TranslatingUtilities {
String getLinkForUrl(String corePath, String s); String getLinkForUrl(String corePath, String s);
} }
public List<ElementDefinition> getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
if (childMapCache .containsKey(element)) { if (childMapCache.containsKey(element)) {
return childMapCache.get(element); return childMapCache.get(element);
} }
StructureDefinition src = profile;
if (element.getContentReference() != null) { if (element.getContentReference() != null) {
List<ElementDefinition> list = null; List<ElementDefinition> list = null;
String id = null; String id = null;
@ -451,6 +469,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (sd == null) { if (sd == null) {
throw new DefinitionException("unable to process contentReference '"+element.getContentReference()+"' on element '"+element.getId()+"'"); throw new DefinitionException("unable to process contentReference '"+element.getContentReference()+"' on element '"+element.getId()+"'");
} }
src = sd;
list = sd.getSnapshot().getElement(); list = sd.getSnapshot().getElement();
id = ref.substring(ref.indexOf("#")+1); id = ref.substring(ref.indexOf("#")+1);
} else { } else {
@ -476,8 +495,9 @@ public class ProfileUtilities extends TranslatingUtilities {
} else } else
break; break;
} }
childMapCache.put(element, res); SourcedChildDefinitions result = new SourcedChildDefinitions(src, res);
return 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); row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE);
} else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) { } else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) {
row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); 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); row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT);
} else { } else {
row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); 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(piece);
c.getPieces().add(gen.new Piece(null, " is prefixed to the value before validation", null)); 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 (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); }
if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { 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, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold"));
c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); 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)) { if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) {
for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) {
if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 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 { private org.hl7.fhir.r5.elementmodel.Element generateExample(StructureDefinition profile, ExampleValueAccessor accessor) throws FHIRException {
ElementDefinition ed = profile.getSnapshot().getElementFirstRep(); 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)); org.hl7.fhir.r5.elementmodel.Element r = new org.hl7.fhir.r5.elementmodel.Element(ed.getPath(), new Property(context, ed, profile));
List<ElementDefinition> children = getChildMap(profile, ed); SourcedChildDefinitions children = getChildMap(profile, ed);
for (ElementDefinition child : children) { for (ElementDefinition child : children.getList()) {
if (child.getPath().endsWith(".id")) { 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)); 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()); id.setValue(profile.getId()+accessor.getId());
@ -6290,8 +6344,8 @@ public class ProfileUtilities extends TranslatingUtilities {
} else { } else {
org.hl7.fhir.r5.elementmodel.Element res = new org.hl7.fhir.r5.elementmodel.Element(tail(ed.getPath()), new Property(context, ed, profile)); 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; boolean hasValue = false;
List<ElementDefinition> children = getChildMap(profile, ed); SourcedChildDefinitions children = getChildMap(profile, ed);
for (ElementDefinition child : children) { for (ElementDefinition child : children.getList()) {
if (!child.hasContentReference()) { if (!child.hasContentReference()) {
org.hl7.fhir.r5.elementmodel.Element e = createExampleElement(profile, child, accessor); org.hl7.fhir.r5.elementmodel.Element e = createExampleElement(profile, child, accessor);
if (e != null) { if (e != null) {

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r5.elementmodel; package org.hl7.fhir.r5.elementmodel;
import java.io.PrintStream;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -75,7 +77,7 @@ public class Element extends Base {
public enum SpecialElement { public enum SpecialElement {
CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER; CONTAINED, BUNDLE_ENTRY, BUNDLE_OUTCOME, PARAMETER, LOGICAL;
public static SpecialElement fromProperty(Property property) { public static SpecialElement fromProperty(Property property) {
if (property.getStructure().getType().equals("Parameters")) if (property.getStructure().getType().equals("Parameters"))
@ -87,7 +89,7 @@ public class Element extends Base {
if (property.getName().equals("contained")) if (property.getName().equals("contained"))
return CONTAINED; return CONTAINED;
if (property.getStructure().getKind() == StructureDefinitionKind.LOGICAL) if (property.getStructure().getKind() == StructureDefinitionKind.LOGICAL)
return CONTAINED; return LOGICAL;
throw new FHIRException("Unknown resource containing a native resource: "+property.getDefinition().getId()); 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 BUNDLE_OUTCOME: return "outcome";
case CONTAINED: return "contained"; case CONTAINED: return "contained";
case PARAMETER: return "parameter"; case PARAMETER: return "parameter";
case LOGICAL: return "logical";
default: return "??"; default: return "??";
} }
} }
@ -124,6 +127,7 @@ public class Element extends Base {
private Map<String, List<Element>> childMap; private Map<String, List<Element>> childMap;
private int descendentCount; private int descendentCount;
private int instanceId; private int instanceId;
private boolean isNull;
public Element(String name) { public Element(String name) {
super(); 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; return value != null;
} }
@ -1127,6 +1139,65 @@ public class Element extends Base {
public void setInstanceId(int instanceId) { public void setInstanceId(int instanceId) {
this.instanceId = 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;
}
} }

View File

@ -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.Extension;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.FHIRPathEngine; 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.StringPair;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
@ -240,7 +241,11 @@ public class JsonParser extends ParserBase {
if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) { if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) {
JsonArray arr = (JsonArray) e; JsonArray arr = (JsonArray) e;
if (arr.size() == 0) { 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; int c = 0;
for (JsonElement am : arr) { for (JsonElement am : arr) {
@ -288,7 +293,7 @@ public class JsonParser extends ParserBase {
String fpathV = fpathArr+"."+propV.getName(); String fpathV = fpathArr+"."+propV.getName();
if (propV.isPrimitive(propV.getType(null))) { if (propV.isPrimitive(propV.getType(null))) {
parseChildPrimitiveInstance(n, propV, propV.getName(), npathV, fpathV, pv.getValue(), 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()); parseChildComplexInstance(npathV, fpathV, n, propV, propV.getName(), pv.getValue());
} else { } else {
logError(line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(pv.getValue())), IssueSeverity.ERROR); 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); n.setPath(fpath);
checkObject(child, npath); checkObject(child, npath);
element.getChildren().add(n); element.getChildren().add(n);
if (property.isResource()) if (property.isResource()) {
parseResource(npath, child, n, property); parseResource(npath, child, n, property);
else } else {
parseChildren(npath, child, n, false); 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); 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) { private String describe(JsonElement e) {

View File

@ -37,6 +37,7 @@ import java.util.List;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; 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.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement; import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.IParser.OutputStyle;
@ -93,8 +94,8 @@ public class ObjectConverter {
if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE)
res.setValue(((PrimitiveType) base).asStringValue()); res.setValue(((PrimitiveType) base).asStringValue());
List<ElementDefinition> children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); SourcedChildDefinitions children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep());
for (ElementDefinition child : children) { for (ElementDefinition child : children.getList()) {
String n = tail(child.getPath()); String n = tail(child.getPath());
if (sd.getKind() != StructureDefinitionKind.PRIMITIVETYPE || !"value".equals(n)) { if (sd.getKind() != StructureDefinitionKind.PRIMITIVETYPE || !"value".equals(n)) {
Base[] values = base.getProperty(n.hashCode(), n, false); Base[] values = base.getProperty(n.hashCode(), n, false);

View File

@ -39,6 +39,7 @@ import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; 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.context.IWorkerContext;
import org.hl7.fhir.r5.formats.FormatUtilities; import org.hl7.fhir.r5.formats.FormatUtilities;
import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition;
@ -229,8 +230,10 @@ public class Property {
} }
public boolean isResource() { public boolean isResource() {
if (definition.getType().size() > 0) if (definition.getType().size() > 0) {
return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode())); String tc = definition.getType().get(0).getCode();
return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames()));
}
else else
return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE); return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE);
} }
@ -300,9 +303,9 @@ public class Property {
protected List<Property> getChildProperties(String elementName, String statedType) throws FHIRException { protected List<Property> getChildProperties(String elementName, String statedType) throws FHIRException {
ElementDefinition ed = definition; ElementDefinition ed = definition;
StructureDefinition sd = structure; StructureDefinition sd = structure;
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed); SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
String url = null; String url = null;
if (children.isEmpty() || isElementWithOnlyExtension(ed, children)) { if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) {
// ok, find the right definitions // ok, find the right definitions
String t = null; String t = null;
if (ed.getType().size() == 1) if (ed.getType().size() == 1)
@ -369,7 +372,7 @@ public class Property {
} }
} }
List<Property> properties = new ArrayList<Property>(); List<Property> properties = new ArrayList<Property>();
for (ElementDefinition child : children) { for (ElementDefinition child : children.getList()) {
properties.add(new Property(context, child, sd, this.profileUtilities)); properties.add(new Property(context, child, sd, this.profileUtilities));
} }
return properties; return properties;
@ -378,8 +381,8 @@ public class Property {
protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException {
ElementDefinition ed = definition; ElementDefinition ed = definition;
StructureDefinition sd = structure; StructureDefinition sd = structure;
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed); SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
if (children.isEmpty()) { if (children.getList().isEmpty()) {
// ok, find the right definitions // ok, find the right definitions
String t = null; String t = null;
if (ed.getType().size() == 1) if (ed.getType().size() == 1)
@ -408,7 +411,7 @@ public class Property {
} }
} }
List<Property> properties = new ArrayList<Property>(); List<Property> properties = new ArrayList<Property>();
for (ElementDefinition child : children) { for (ElementDefinition child : children.getList()) {
properties.add(new Property(context, child, sd, this.profileUtilities)); properties.add(new Property(context, child, sd, this.profileUtilities));
} }
return properties; 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;
}
}
} }

View File

@ -27,6 +27,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; 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.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
@ -5767,10 +5768,10 @@ public class FHIRPathEngine {
if (expr.getName().equals("$this")) { if (expr.getName().equals("$this")) {
focus = element; focus = element;
} else { } else {
List<ElementDefinition> childDefinitions; SourcedChildDefinitions childDefinitions;
childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); childDefinitions = profileUtilities.getChildMap(sd, element.getElement());
// if that's empty, get the children of the type // if that's empty, get the children of the type
if (childDefinitions.isEmpty()) { if (childDefinitions.getList().isEmpty()) {
sd = fetchStructureByType(element, expr); sd = fetchStructureByType(element, expr);
if (sd == null) { if (sd == null) {
@ -5778,7 +5779,7 @@ public class FHIRPathEngine {
} }
childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); 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) 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); focus = new TypedElementDefinition(t);
break; break;
@ -5806,8 +5807,8 @@ public class FHIRPathEngine {
focus = new TypedElementDefinition(sd.getSnapshot().getElementFirstRep()); focus = new TypedElementDefinition(sd.getSnapshot().getElementFirstRep());
} else if ("extension".equals(expr.getName())) { } else if ("extension".equals(expr.getName())) {
String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue(); String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue();
List<ElementDefinition> childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement());
for (ElementDefinition t : childDefinitions) { for (ElementDefinition t : childDefinitions.getList()) {
if (t.getPath().endsWith(".extension") && t.hasSliceName()) { if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
System.out.println("t: "+t.getId()); System.out.println("t: "+t.getId());
StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ? 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()); exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition());
} }
if (exsd != null && exsd.getUrl().equals(targetUrl)) { if (exsd != null && exsd.getUrl().equals(targetUrl)) {
if (profileUtilities.getChildMap(sd, t).isEmpty()) { if (profileUtilities.getChildMap(sd, t).getList().isEmpty()) {
sd = exsd; sd = exsd;
} }
focus = new TypedElementDefinition(t); focus = new TypedElementDefinition(t);

View File

@ -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_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_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_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_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_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 // 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"; 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_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_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"; public static final String EXT_MAPPING_TGTCARD = "http://hl7.org/fhir/tools/StructureDefinition/conceptmap-target-cardinality";
// specific extension helpers // specific extension helpers
public static Extension makeIssueSource(Source source) { public static Extension makeIssueSource(Source source) {
@ -482,6 +484,8 @@ public class ToolingExtensions {
return false; return false;
if (!(ex.getValue() instanceof BooleanType)) if (!(ex.getValue() instanceof BooleanType))
return false; return false;
if (!(ex.getValue().hasPrimitiveValue()))
return false;
return ((BooleanType) ex.getValue()).getValue(); return ((BooleanType) ex.getValue()).getValue();
} }

View File

@ -2516,7 +2516,7 @@ public class StructureMapUtilities {
private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException { private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException {
boolean first = true; boolean first = true;
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed); List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed).getList();
for (ElementDefinition child : children) { for (ElementDefinition child : children) {
if (first && inner) { if (first && inner) {
b.append(" then {\r\n"); b.append(" then {\r\n");

View File

@ -1,7 +1,27 @@
package org.hl7.fhir.r5.utils.validation.constants; package org.hl7.fhir.r5.utils.validation.constants;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.Utilities;
public enum IdStatus { public enum IdStatus {
OPTIONAL, OPTIONAL,
REQUIRED, 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+"'");
}
}
}
} }

View File

@ -80,6 +80,7 @@ public class I18nConstants {
public static final String DUPLICATE_ID = "DUPLICATE_ID"; public static final String DUPLICATE_ID = "DUPLICATE_ID";
public static final String DUPLICATE_RESOURCE_ = "Duplicate_Resource_"; public static final String DUPLICATE_RESOURCE_ = "Duplicate_Resource_";
public static final String DUPLICATE_RESOURCE_VERSION = "DUPLICATE_RESOURCE_VERSION"; 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_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_MUST_HAVE_SOME_CONTENT = "Element_must_have_some_content";
public static final String ELEMENT__NULL_ = "element__null_"; public static final String ELEMENT__NULL_ = "element__null_";

View File

@ -437,8 +437,10 @@ public class JsonTrackingParser {
else else
throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString()); throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
parseProperty(); if (lexer.getType() != TokenType.Close) {
readObject(result, true); parseProperty();
readObject(result, true);
}
if (map != null) if (map != null)
map.put(result, loc); map.put(result, loc);
return result; return result;

View File

@ -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_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_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_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'
} }

View File

@ -62,6 +62,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.conformance.ProfileUtilities; 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.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; 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 EXECUTION_ID = "validator.execution.id";
private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)"; private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)";
private static final boolean STACK_TRACE = false; private static final boolean STACK_TRACE = false;
private static final boolean DEBUG_ELEMENT = false;
private class ValidatorHostServices implements IEvaluationContext { private class ValidatorHostServices implements IEvaluationContext {
@ -863,6 +865,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
errors.removeAll(messagesToRemove); errors.removeAll(messagesToRemove);
timeTracker.overall(t); timeTracker.overall(t);
if (DEBUG_ELEMENT) {
element.printToOutput();
}
} }
@ -3622,8 +3627,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
timeTracker.sd(t); 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; 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; return null;
} }
@ -4854,49 +4871,56 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ElementDefinition child, ElementDefinition context, Element resource, ElementDefinition child, ElementDefinition context, Element resource,
Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct) throws FHIRException { Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct) throws FHIRException {
SpecialElement special = element.getSpecial(); if (element.isNull()) {
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), ToolingExtensions.readBooleanExtension(child, ToolingExtensions.EXT_JSON_NULLABLE),
ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? I18nConstants.ELEMENT_CANNOT_BE_NULL)) {
ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, // nothing else to validate?
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.qualifyPath(".ofType("+resourceName+")"); } else {
SpecialElement special = element.getSpecial();
if (typeForResource == null) { ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ?
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this,
false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); hostContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl());
} else if (isValidResourceType(resourceName, typeForResource)) {
if (containedValidationPolicy.checkValid()) { if (containedValidationPolicy.ignore()) {
// special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise return;
ValidatorHostContext hc = null; }
if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.PARAMETER) {
resource = element; String resourceName = element.getType();
assert Utilities.existsInList(hostContext.getRootResource().fhirType(), "Bundle", "Parameters") : "Resource is "+hostContext.getRootResource().fhirType()+", expected Bundle or Parameters"; TypeRefComponent typeForResource = null;
hc = hostContext.forEntry(element, hostContext.getRootResource()); // root becomes the grouping resource (should be either bundle or parameters) CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder();
} else {
hc = hostContext.forContained(element); // 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(); stack.qualifyPath(".ofType("+resourceName+")");
if (special != null) {
switch (special) { 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_ENTRY:
case BUNDLE_OUTCOME: case BUNDLE_OUTCOME:
case PARAMETER: case PARAMETER:
@ -4908,51 +4932,52 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
break; break;
default: default:
break; break;
}
} }
}
if (typeForResource.getProfile().size() == 1) { if (typeForResource.getProfile().size() == 1) {
long t = System.nanoTime(); long t = System.nanoTime();
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue()); StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue());
timeTracker.sd(t); timeTracker.sd(t);
trackUsage(profile, hostContext, element); trackUsage(profile, hostContext, element);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), 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())) { 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); 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<String> 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 { } else {
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), List<String> types = new ArrayList<>();
false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); 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 // get the list of direct defined children, including slices
List<ElementDefinition> childDefinitions = profileUtilities.getChildMap(profile, definition); SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(profile, definition);
if (childDefinitions.isEmpty()) { if (childDefinitions.getList().isEmpty()) {
if (actualType == null) if (actualType == null)
return; // there'll be an error elsewhere in this case, and we're going to stop. return; // there'll be an error elsewhere in this case, and we're going to stop.
childDefinitions = getActualTypeChildren(hostContext, element, actualType); 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. // this only happens when the profile constrains the abstract children but leaves th choice open.
if (actualType == null) if (actualType == null)
return; // there'll be an error elsewhere in this case, and we're going to stop. return; // there'll be an error elsewhere in this case, and we're going to stop.
List<ElementDefinition> 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) // 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); childDefinitions = mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType);
} }
@ -5045,27 +5070,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
private List<ElementDefinition> mergeChildLists(List<ElementDefinition> source, List<ElementDefinition> additional, String masterPath, String typePath) { private SourcedChildDefinitions mergeChildLists(SourcedChildDefinitions source, SourcedChildDefinitions additional, String masterPath, String typePath) {
List<ElementDefinition> res = new ArrayList<>(); SourcedChildDefinitions res = new SourcedChildDefinitions(additional.getSource(), new ArrayList<>());
res.addAll(source); res.getList().addAll(source.getList());
for (ElementDefinition ed : additional) { for (ElementDefinition ed : additional.getList()) {
boolean inMaster = false; boolean inMaster = false;
for (ElementDefinition t : source) { for (ElementDefinition t : source.getList()) {
String tp = masterPath + ed.getPath().substring(typePath.length()); String tp = masterPath + ed.getPath().substring(typePath.length());
if (t.getPath().equals(tp)) { if (t.getPath().equals(tp)) {
inMaster = true; inMaster = true;
} }
} }
if (!inMaster) { if (!inMaster) {
res.add(ed); res.getList().add(ed);
} }
} }
return res; return res;
} }
// todo: the element definition in context might assign a constrained profile for the type? // todo: the element definition in context might assign a constrained profile for the type?
public List<ElementDefinition> getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { public SourcedChildDefinitions getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) {
List<ElementDefinition> childDefinitions; SourcedChildDefinitions childDefinitions;
StructureDefinition dt = null; StructureDefinition dt = null;
if (isAbsolute(actualType)) if (isAbsolute(actualType))
dt = this.context.fetchResource(StructureDefinition.class, 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())) { && !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) {
type = checkDefn.getType().get(0).getWorkingCode(); type = checkDefn.getType().get(0).getWorkingCode();
String stype = ei.getElement().fhirType(); String stype = ei.getElement().fhirType();
if (checkDefn.isChoice() && !stype.equals(type)) { if (!stype.equals(type)) {
if (extensionUrl != null && !isAbsolute(extensionUrl)) { if (checkDefn.isChoice()) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); if (extensionUrl != null && !isAbsolute(extensionUrl)) {
} else if (!isAbstractType(type) && !"Extension".equals(profile.getType())) { rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype);
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) && !"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) || } 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 (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 // 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())); 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) { if (debug) {
System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()+time()); System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()+time());
} }
String localStackLiteralPath = localStack.getLiteralPath(); String localStackLiteralPath = localStack.getLiteralPath();
String eiPath = ei.getPath(); String eiPath = ei.getPath();
if (!eiPath.equals(localStackLiteralPath)) { if (!eiPath.equals(localStackLiteralPath)) {
@ -5450,9 +5482,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public void checkCardinalities(List<ValidationMessage> errors, StructureDefinition profile, Element element, NodeStack stack, public void checkCardinalities(List<ValidationMessage> errors, StructureDefinition profile, Element element, NodeStack stack,
List<ElementDefinition> childDefinitions, List<ElementInfo> children, List<String> problematicPaths) throws DefinitionException { SourcedChildDefinitions childDefinitions, List<ElementInfo> children, List<String> problematicPaths) throws DefinitionException {
// 3. report any definitions that have a cardinality problem // 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 if (ed.getRepresentation().isEmpty()) { // ignore xml attributes
int count = 0; int count = 0;
List<ElementDefinition> slices = null; List<ElementDefinition> slices = null;
@ -5490,7 +5522,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public List<String> assignChildren(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource, public List<String> assignChildren(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource,
NodeStack stack, List<ElementDefinition> childDefinitions, List<ElementInfo> children) throws DefinitionException { NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children) throws DefinitionException {
// 2. assign children to a definition // 2. assign children to a definition
// for each definition, for each child, check whether it belongs in the slice // for each definition, for each child, check whether it belongs in the slice
ElementDefinition slicer = null; ElementDefinition slicer = null;
@ -5498,8 +5530,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
List<String> problematicPaths = new ArrayList<String>(); List<String> problematicPaths = new ArrayList<String>();
String slicingPath = null; String slicingPath = null;
int sliceOffset = 0; int sliceOffset = 0;
for (int i = 0; i < childDefinitions.size(); i++) { for (int i = 0; i < childDefinitions.getList().size(); i++) {
ElementDefinition ed = childDefinitions.get(i); ElementDefinition ed = childDefinitions.getList().get(i);
boolean childUnsupportedSlicing = false; boolean childUnsupportedSlicing = false;
boolean process = true; boolean process = true;
if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) { if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) {
@ -5553,7 +5585,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else { } else {
// Don't raise this if we're in an abstract profile, like Resource // 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()); 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) { 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 req = ep.getNamedChild("request");
Element resp = ep.getNamedChild("response"); Element resp = ep.getNamedChild("response");
Element fullUrl = ep.getNamedChild(FULL_URL); Element fullUrl = ep.getNamedChild(FULL_URL);

View File

@ -126,8 +126,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
private JsonObject content; private JsonObject content;
private String version; private String version;
private String name; private String name;
private static StringBuilder logB = new StringBuilder();
private static Map<String, ValidationEngine> ve = new HashMap<>(); private static Map<String, ValidationEngine> ve = new HashMap<>();
@ -145,7 +144,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
CacheVerificationLogger logger = new CacheVerificationLogger(); CacheVerificationLogger logger = new CacheVerificationLogger();
long setup = System.nanoTime(); long setup = System.nanoTime();
logOutputToFile("---- " + name + " ---------------------------------------------------------------- ("+System.getProperty("java.vm.name")+")"); logOutput("---- " + name + " ---------------------------------------------------------------- ("+System.getProperty("java.vm.name")+")");
logOutput("** Core: "); logOutput("** Core: ");
String txLog = null; String txLog = null;
if (content.has("txLog")) { if (content.has("txLog")) {
@ -220,7 +219,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
if (content.has("packages")) { if (content.has("packages")) {
for (JsonElement e : content.getAsJsonArray("packages")) { for (JsonElement e : content.getAsJsonArray("packages")) {
String n = e.getAsString(); String n = e.getAsString();
logOutputToFile("load package "+n); logOutput("load package "+n);
InputStream cnt = n.endsWith(".tgz") ? TestingUtilities.loadTestResourceStream("validator", n) : null; InputStream cnt = n.endsWith(".tgz") ? TestingUtilities.loadTestResourceStream("validator", n) : null;
if (cnt != null) { if (cnt != null) {
igLoader.loadPackage(NpmPackage.fromPackage(cnt), true); igLoader.loadPackage(NpmPackage.fromPackage(cnt), true);
@ -237,7 +236,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
String filename = e.getAsString(); String filename = e.getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename); String contents = TestingUtilities.loadTestResource("validator", filename);
CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); CanonicalResource mr = (CanonicalResource) loadResource(filename, contents);
logOutputToFile("load resource "+mr.getUrl()); logOutput("load resource "+mr.getUrl());
val.getContext().cacheResource(mr); val.getContext().cacheResource(mr);
if (mr instanceof ImplementationGuide) { if (mr instanceof ImplementationGuide) {
val.getImplementationGuides().add((ImplementationGuide) mr); val.getImplementationGuides().add((ImplementationGuide) mr);
@ -253,7 +252,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
String filename = je.getAsString(); String filename = je.getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename); String contents = TestingUtilities.loadTestResource("validator", filename);
StructureDefinition sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext()); StructureDefinition sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext());
logOutputToFile("load resource "+sd.getUrl()); logOutput("load resource "+sd.getUrl());
val.getContext().cacheResource(sd); val.getContext().cacheResource(sd);
} }
} }
@ -289,7 +288,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
JsonObject profile = content.getAsJsonObject("profile"); JsonObject profile = content.getAsJsonObject("profile");
if (profile.has("packages")) { if (profile.has("packages")) {
for (JsonElement e : profile.getAsJsonArray("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); igLoader.loadIg(vCurr.getIgs(), vCurr.getBinaries(), e.getAsString(), true);
} }
} }
@ -301,7 +300,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
String filename = e.getAsString(); String filename = e.getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename); String contents = TestingUtilities.loadTestResource("validator", filename);
CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); CanonicalResource mr = (CanonicalResource) loadResource(filename, contents);
logOutputToFile("load resource "+mr.getUrl()); logOutput("load resource "+mr.getUrl());
val.getContext().cacheResource(mr); val.getContext().cacheResource(mr);
if (mr instanceof ImplementationGuide) { if (mr instanceof ImplementationGuide) {
val.getImplementationGuides().add((ImplementationGuide) mr); val.getImplementationGuides().add((ImplementationGuide) mr);
@ -317,7 +316,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
logOutput("Name: " + name + " - profile : " + profile.get("source").getAsString()); logOutput("Name: " + name + " - profile : " + profile.get("source").getAsString());
version = content.has("version") ? content.get("version").getAsString() : version; version = content.has("version") ? content.get("version").getAsString() : version;
sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext()); sd = loadProfile(filename, contents, messages, val.isDebug(), val.getContext());
logOutputToFile("load resource "+sd.getUrl()); logOutput("load resource "+sd.getUrl());
val.getContext().cacheResource(sd); val.getContext().cacheResource(sd);
} }
val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false); val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false);
@ -338,13 +337,13 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
if (mr instanceof StructureDefinition) { if (mr instanceof StructureDefinition) {
new ContextUtilities(val.getContext()).generateSnapshot((StructureDefinition) mr, true); new ContextUtilities(val.getContext()).generateSnapshot((StructureDefinition) mr, true);
} }
logOutputToFile("load resource "+mr.getUrl()); logOutput("load resource "+mr.getUrl());
val.getContext().cacheResource(mr); val.getContext().cacheResource(mr);
} }
} }
if (logical.has("packages")) { if (logical.has("packages")) {
for (JsonElement e : logical.getAsJsonArray("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); igLoader.loadIg(vCurr.getIgs(), vCurr.getBinaries(), e.getAsString(), true);
} }
} }
@ -557,13 +556,6 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
System.out.println(msg); 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) { private OperationOutcomeIssueComponent findMatchingIssue(OperationOutcome oo, OperationOutcomeIssueComponent iss) {
for (OperationOutcomeIssueComponent t : oo.getIssue()) { 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() if (t.getExpression().get(0).getValue().equals(iss.getExpression().get(0).getValue()) && t.getCode() == iss.getCode() && t.getSeverity() == iss.getSeverity()

View File

@ -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)" "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"
}
-------------------------------------------------------------------------------------

View File

@ -1966,3 +1966,14 @@ v: {
"system" : "http://loinc.org" "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"
}
-------------------------------------------------------------------------------------

View File

@ -9,3 +9,104 @@ v: {
"system" : "http://www.nlm.nih.gov/research/umls/rxnorm" "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"
}
-------------------------------------------------------------------------------------

View File

@ -2002,3 +2002,57 @@ v: {
"system" : "http://snomed.info/sct" "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"
}
-------------------------------------------------------------------------------------

View File

@ -205,3 +205,23 @@ v: {
"error" : "The code provided (http://unitsofmeasure.org#m) is not valid (from http://tx.fhir.org/r4)" "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"
}
-------------------------------------------------------------------------------------