Merge pull request #960 from hapifhir/gg-202210-cdshooks

Gg 202210 cdshooks
This commit is contained in:
Grahame Grieve 2022-10-21 17:04:44 +11:00 committed by GitHub
commit bff317bc39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 596 additions and 172 deletions

View File

@ -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<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 {
private StructureDefinition source;
@ -355,7 +372,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private XVerExtensionManager xver;
private boolean wantFixDifferentialFirstElementType;
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<>();
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);
}
public List<ElementDefinition> 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<ElementDefinition> 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<ElementDefinition> 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<ElementDefinition> 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) {

View File

@ -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<String, List<Element>> 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;
}
}

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.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) {

View File

@ -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<ElementDefinition> 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);

View File

@ -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<Property> getChildProperties(String elementName, String statedType) throws FHIRException {
ElementDefinition ed = definition;
StructureDefinition sd = structure;
List<ElementDefinition> 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<Property> properties = new ArrayList<Property>();
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<Property> getChildProperties(TypeDetails type) throws DefinitionException {
ElementDefinition ed = definition;
StructureDefinition sd = structure;
List<ElementDefinition> 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<Property> properties = new ArrayList<Property>();
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;
}
}
}

View File

@ -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;
}

View File

@ -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<ElementDefinition> 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<ElementDefinition> 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);

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_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();
}

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 {
boolean first = true;
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed);
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed).getList();
for (ElementDefinition child : children) {
if (first && inner) {
b.append(" then {\r\n");

View File

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

View File

@ -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)) {

View File

@ -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_";

View File

@ -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;

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

View File

@ -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<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 {
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(),
false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types);
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 {
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<ElementDefinition> 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<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)
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) {
List<ElementDefinition> 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<ElementDefinition> getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) {
List<ElementDefinition> 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<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
for (ElementDefinition ed : childDefinitions) {
for (ElementDefinition ed : childDefinitions.getList()) {
if (ed.getRepresentation().isEmpty()) { // ignore xml attributes
int count = 0;
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,
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
// 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<String> problematicPaths = new ArrayList<String>();
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);

View File

@ -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<String, ValidationEngine> 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()

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