enforce constraints in slicer as well as in slice + Handle -tx n/a better

This commit is contained in:
Grahame Grieve 2020-10-14 13:16:59 +11:00
parent d20582ca9d
commit 920c3193d0
3 changed files with 269 additions and 229 deletions

View File

@ -49,6 +49,7 @@ import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.UcumService; import org.fhir.ucum.UcumService;
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.NoTerminologyServiceException;
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.context.CanonicalResourceManager.CanonicalResourceProxy; import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
@ -910,6 +911,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT); txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res; return res;
} catch (Exception e) { } catch (Exception e) {
if (e instanceof NoTerminologyServiceException) {
return new ValidationResult(IssueSeverity.ERROR, "No Terminology Service", TerminologyServiceErrorClass.NOSERVICE);
}
} }
} }

View File

@ -37,6 +37,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.NoTerminologyServiceException;
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;
import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CanonicalType;
@ -515,6 +516,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) { if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
return null; return null;
} }
if (res.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
throw new NoTerminologyServiceException();
}
return res.isOk(); return res.isOk();
} else { } else {
if (vsi.hasFilter()) { if (vsi.hasFilter()) {

View File

@ -37,10 +37,8 @@ import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -51,15 +49,10 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.codec.binary.Base64InputStream; import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.convertors.*;
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.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;
@ -86,6 +79,7 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ContactPoint; import org.hl7.fhir.r5.model.ContactPoint;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.DateType; import org.hl7.fhir.r5.model.DateType;
import org.hl7.fhir.r5.model.DecimalType; import org.hl7.fhir.r5.model.DecimalType;
@ -114,6 +108,7 @@ import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Quantity; import org.hl7.fhir.r5.model.Quantity;
import org.hl7.fhir.r5.model.Range; import org.hl7.fhir.r5.model.Range;
import org.hl7.fhir.r5.model.Ratio; import org.hl7.fhir.r5.model.Ratio;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.SampledData; import org.hl7.fhir.r5.model.SampledData;
import org.hl7.fhir.r5.model.SearchParameter; import org.hl7.fhir.r5.model.SearchParameter;
@ -127,18 +122,30 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComp
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.TimeType; import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.Timing; import org.hl7.fhir.r5.model.Timing;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.Validator.QuestionnaireMode; import org.hl7.fhir.validation.Validator.QuestionnaireMode;
import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.BundleValidator;
@ -148,19 +155,13 @@ import org.hl7.fhir.validation.instance.type.QuestionnaireValidator;
import org.hl7.fhir.validation.instance.type.SearchParameterValidator; import org.hl7.fhir.validation.instance.type.SearchParameterValidator;
import org.hl7.fhir.validation.instance.type.StructureDefinitionValidator; import org.hl7.fhir.validation.instance.type.StructureDefinitionValidator;
import org.hl7.fhir.validation.instance.type.ValueSetValidator; import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.validation.instance.utils.ChildIterator;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.validation.instance.utils.ElementInfo;
import org.hl7.fhir.utilities.Utilities.DecimalStatus; import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo; import org.hl7.fhir.validation.instance.utils.ResolvedReference;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.instance.utils.ResourceValidationTracker;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.validation.instance.utils.ValidatorHostContext;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.validation.instance.utils.*;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -343,6 +344,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private String validationLanguage; private String validationLanguage;
private boolean baseOnly; private boolean baseOnly;
private boolean noCheckAggregation; private boolean noCheckAggregation;
private boolean wantCheckSnapshotUnchanged;
private List<ImplementationGuide> igs = new ArrayList<>(); private List<ImplementationGuide> igs = new ArrayList<>();
private List<String> extensionDomains = new ArrayList<String>(); private List<String> extensionDomains = new ArrayList<String>();
@ -1004,7 +1006,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, true); // we're going to validate the codings directly, so only check the valueset ValidationResult vr = checkCodeOnServer(stack, valueset, cc, true); // we're going to validate the codings directly, so only check the valueset
if (!vr.isOk()) { if (!vr.isOk()) {
bindingsOk = false; bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
if (binding.getStrength() == BindingStrength.REQUIRED || (binding.getStrength() == BindingStrength.EXTENSIBLE && binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))) {
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_REQ, describeReference(binding.getValueSet()));
} else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_EXT, describeReference(binding.getValueSet()));
}
} else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
if (binding.getStrength() == BindingStrength.REQUIRED) if (binding.getStrength() == BindingStrength.REQUIRED)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString());
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
@ -3860,7 +3868,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (element.getType().equals("SearchParameter")) { } else if (element.getType().equals("SearchParameter")) {
new SearchParameterValidator(context, timeTracker, fpe).validateSearchParameter(errors, element, stack); new SearchParameterValidator(context, timeTracker, fpe).validateSearchParameter(errors, element, stack);
} else if (element.getType().equals("StructureDefinition")) { } else if (element.getType().equals("StructureDefinition")) {
new StructureDefinitionValidator(context, timeTracker, fpe).validateStructureDefinition(errors, element, stack); new StructureDefinitionValidator(context, timeTracker, fpe, wantCheckSnapshotUnchanged).validateStructureDefinition(errors, element, stack);
} else if (element.getType().equals("ValueSet")) { } else if (element.getType().equals("ValueSet")) {
new ValueSetValidator(context, timeTracker, this).validateValueSet(errors, element, stack); new ValueSetValidator(context, timeTracker, this).validateValueSet(errors, element, stack);
} }
@ -4128,236 +4136,252 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl)
throws FHIRException, DefinitionException { throws FHIRException, DefinitionException {
List<String> profiles = new ArrayList<String>();
if (ei.definition != null) { if (ei.definition != null) {
String type = null; checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei,
ElementDefinition typeDefn = null; extensionUrl, ei.definition, false);
checkMustSupport(profile, ei); }
if (ei.slice != null) {
checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei,
extensionUrl, ei.slice, true);
}
}
if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getWorkingCode()) && !"Element".equals(ei.definition.getType().get(0).getWorkingCode()) public void checkChildByDefinition(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile,
&& !"BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode())) { ElementDefinition definition, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept,
type = ei.definition.getType().get(0).getWorkingCode(); boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, ElementDefinition checkDefn, boolean isSlice) {
String stype = ei.getElement().fhirType(); List<String> profiles = new ArrayList<String>();
if (ei.definition.isChoice() && !stype.equals(type)) { String type = null;
if ("Extension".equals(profile.getType())) { ElementDefinition typeDefn = null;
// error will be raised elsewhere checkMustSupport(profile, ei);
} else {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype);
}
}
//
// Excluding reference is a kludge to get around versioning issues
if (ei.definition.getType().get(0).hasProfile()) {
for (CanonicalType p : ei.definition.getType().get(0).getProfile()) {
profiles.add(p.getValue());
}
}
} else if (ei.definition.getType().size() == 1 && "*".equals(ei.definition.getType().get(0).getWorkingCode())) {
String prefix = tail(ei.definition.getPath());
assert prefix.endsWith("[x]");
type = ei.getName().substring(prefix.length() - 3);
if (isPrimitiveType(type))
type = Utilities.uncapitalize(type);
if (ei.definition.getType().get(0).hasProfile()) {
for (CanonicalType p : ei.definition.getType().get(0).getProfile()) {
profiles.add(p.getValue());
}
}
} else if (ei.definition.getType().size() > 1) {
String prefix = tail(ei.definition.getPath()); if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode())
assert typesAreAllReference(ei.definition.getType()) || ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; && !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) {
type = checkDefn.getType().get(0).getWorkingCode();
if (ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR)) String stype = ei.getElement().fhirType();
type = ei.getElement().getType(); if (checkDefn.isChoice() && !stype.equals(type)) {
else { if ("Extension".equals(profile.getType())) {
prefix = prefix.substring(0, prefix.length() - 3); // error will be raised elsewhere
for (TypeRefComponent t : ei.definition.getType()) } else {
if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype);
type = t.getWorkingCode();
// Excluding reference is a kludge to get around versioning issues
if (t.hasProfile() && !type.equals("Reference"))
profiles.add(t.getProfile().get(0).getValue());
}
}
if (type == null) {
TypeRefComponent trc = ei.definition.getType().get(0);
if (trc.getWorkingCode().equals("Reference"))
type = "Reference";
else
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTYPE, ei.getName(), describeTypes(ei.definition.getType()));
}
} else if (ei.definition.getContentReference() != null) {
typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference());
} else if (ei.definition.getType().size() == 1 && ("Element".equals(ei.definition.getType().get(0).getWorkingCode()) || "BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode()))) {
if (ei.definition.getType().get(0).hasProfile()) {
CanonicalType pu = ei.definition.getType().get(0).getProfile().get(0);
if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT))
profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT));
else
profiles.add(pu.getValue());
} }
} }
//
if (type != null) { // Excluding reference is a kludge to get around versioning issues
if (type.startsWith("@")) { if (checkDefn.getType().get(0).hasProfile()) {
ei.definition = findElement(profile, type.substring(1)); for (CanonicalType p : checkDefn.getType().get(0).getProfile()) {
type = null; profiles.add(p.getValue());
} }
} }
NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); } else if (checkDefn.getType().size() == 1 && "*".equals(checkDefn.getType().get(0).getWorkingCode())) {
String prefix = tail(checkDefn.getPath());
assert prefix.endsWith("[x]");
type = ei.getName().substring(prefix.length() - 3);
if (isPrimitiveType(type))
type = Utilities.uncapitalize(type);
if (checkDefn.getType().get(0).hasProfile()) {
for (CanonicalType p : checkDefn.getType().get(0).getProfile()) {
profiles.add(p.getValue());
}
}
} else if (checkDefn.getType().size() > 1) {
String prefix = tail(checkDefn.getPath());
assert typesAreAllReference(checkDefn.getType()) || checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix;
if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR))
type = ei.getElement().getType();
else {
prefix = prefix.substring(0, prefix.length() - 3);
for (TypeRefComponent t : checkDefn.getType())
if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) {
type = t.getWorkingCode();
// Excluding reference is a kludge to get around versioning issues
if (t.hasProfile() && !type.equals("Reference"))
profiles.add(t.getProfile().get(0).getValue());
}
}
if (type == null) {
TypeRefComponent trc = checkDefn.getType().get(0);
if (trc.getWorkingCode().equals("Reference"))
type = "Reference";
else
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTYPE, ei.getName(), describeTypes(checkDefn.getType()));
}
} else if (checkDefn.getContentReference() != null) {
typeDefn = resolveNameReference(profile.getSnapshot(), checkDefn.getContentReference());
} else if (checkDefn.getType().size() == 1 && ("Element".equals(checkDefn.getType().get(0).getWorkingCode()) || "BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode()))) {
if (checkDefn.getType().get(0).hasProfile()) {
CanonicalType pu = checkDefn.getType().get(0).getProfile().get(0);
if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT))
profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT));
else
profiles.add(pu.getValue());
}
}
if (type != null) {
if (type.startsWith("@")) {
checkDefn = findElement(profile, type.substring(1));
if (isSlice) {
ei.slice = ei.definition;
} else {
ei.definition = ei.definition;
}
type = null;
}
}
NodeStack localStack = stack.push(ei.getElement(), 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()); // System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl());
// } // }
String localStackLiterapPath = localStack.getLiteralPath(); String localStackLiterapPath = localStack.getLiteralPath();
String eiPath = ei.getPath(); String eiPath = ei.getPath();
assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath;
boolean thisIsCodeableConcept = false; boolean thisIsCodeableConcept = false;
String thisExtension = null; String thisExtension = null;
boolean checkDisplay = true; boolean checkDisplay = true;
checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : ei.definition, resource, ei.getElement(), localStack, true); checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, true);
ei.getElement().markValidation(profile, ei.definition); ei.getElement().markValidation(profile, checkDefn);
boolean elementValidated = false; boolean elementValidated = false;
if (type != null) { if (type != null) {
if (isPrimitiveType(type)) { if (isPrimitiveType(type)) {
checkPrimitive(hostContext, errors, ei.getPath(), type, ei.definition, ei.getElement(), profile, stack); checkPrimitive(hostContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, stack);
} else {
if (ei.definition.hasFixed()) {
checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getFixed(), profile.getUrl(), ei.definition.getSliceName(), null);
}
if (ei.definition.hasPattern()) {
checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getPattern(), profile.getUrl(), ei.definition.getSliceName(), null, true);
}
}
if (type.equals("Identifier")) {
checkIdentifier(errors, ei.getPath(), ei.getElement(), ei.definition);
} else if (type.equals("Coding")) {
checkCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("Quantity")) {
checkQuantity(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack);
} else if (type.equals("Attachment")) {
checkAttachment(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("CodeableConcept")) {
checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack);
thisIsCodeableConcept = true;
} else if (type.equals("Reference")) {
checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, ei.definition, actualType, localStack);
// We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension
} else if (type.equals("Extension")) {
Element eurl = ei.getElement().getNamedChild("url");
if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
String url = eurl.primitiveValue();
thisExtension = url;
if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) {
checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl);
}
}
}
} else if (type.equals("Resource") || isResource(type)) {
validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if
elementValidated = true;
// (str.matches(".*([.,/])work\\1$"))
} else if (Utilities.isAbsoluteUrl(type)) {
StructureDefinition defn = context.fetchTypeDefinition(type);
if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) {
List<String> txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep());
if (txtype.contains("CodeableConcept")) {
checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack, defn);
thisIsCodeableConcept = true;
} else if (txtype.contains("Coding")) {
checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn);
}
}
}
} else { } else {
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName())) if (checkDefn.hasFixed()) {
validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getUrl(), checkDefn.getSliceName(), null);
}
StructureDefinition p = null;
String tail = null;
if (profiles.isEmpty()) {
if (type != null) {
p = getProfileForType(type, ei.definition.getType());
// If dealing with a primitive type, then we need to check the current child against
// the invariants (constraints) on the current element, because otherwise it only gets
// checked against the primary type's invariants: LLoyd
//if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
// checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element);
//}
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_NOTYPE, type);
} }
} else if (profiles.size() == 1) { if (checkDefn.hasPattern()) {
String url = profiles.get(0); checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getPattern(), profile.getUrl(), checkDefn.getSliceName(), null, true);
}
}
if (type.equals("Identifier")) {
checkIdentifier(errors, ei.getPath(), ei.getElement(), checkDefn);
} else if (type.equals("Coding")) {
checkCoding(errors, ei.getPath(), ei.getElement(), profile, checkDefn, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("Quantity")) {
checkQuantity(errors, ei.getPath(), ei.getElement(), profile, checkDefn, stack);
} else if (type.equals("Attachment")) {
checkAttachment(errors, ei.getPath(), ei.getElement(), profile, checkDefn, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("CodeableConcept")) {
checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, checkDefn, stack);
thisIsCodeableConcept = true;
} else if (type.equals("Reference")) {
checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, checkDefn, actualType, localStack);
// We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension
} else if (type.equals("Extension")) {
Element eurl = ei.getElement().getNamedChild("url");
if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
String url = eurl.primitiveValue();
thisExtension = url;
if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) {
checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), checkDefn, profile, localStack, stack, extensionUrl);
}
}
}
} else if (type.equals("Resource") || isResource(type)) {
validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if
elementValidated = true;
// (str.matches(".*([.,/])work\\1$"))
} else if (Utilities.isAbsoluteUrl(type)) {
StructureDefinition defn = context.fetchTypeDefinition(type);
if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) {
List<String> txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep());
if (txtype.contains("CodeableConcept")) {
checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, checkDefn, stack, defn);
thisIsCodeableConcept = true;
} else if (txtype.contains("Coding")) {
checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, checkDefn, inCodeableConcept, checkDisplayInContext, stack, defn);
}
}
}
} else {
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), checkDefn != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName()))
validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, false, true, null);
}
StructureDefinition p = null;
String tail = null;
if (profiles.isEmpty()) {
if (type != null) {
p = getProfileForType(type, checkDefn.getType());
// If dealing with a primitive type, then we need to check the current child against
// the invariants (constraints) on the current element, because otherwise it only gets
// checked against the primary type's invariants: LLoyd
//if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
// checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element);
//}
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_NOTYPE, type);
}
} else if (profiles.size() == 1) {
String url = profiles.get(0);
if (url.contains("#")) {
tail = url.substring(url.indexOf("#") + 1);
url = url.substring(0, url.indexOf("#"));
}
p = this.context.fetchResource(StructureDefinition.class, url);
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, profiles.get(0));
} else {
elementValidated = true;
HashMap<String, List<ValidationMessage>> goodProfiles = new HashMap<String, List<ValidationMessage>>();
HashMap<String, List<ValidationMessage>> badProfiles = new HashMap<String, List<ValidationMessage>>();
for (String typeProfile : profiles) {
String url = typeProfile;
tail = null;
if (url.contains("#")) { if (url.contains("#")) {
tail = url.substring(url.indexOf("#") + 1); tail = url.substring(url.indexOf("#") + 1);
url = url.substring(0, url.indexOf("#")); url = url.substring(0, url.indexOf("#"));
} }
p = this.context.fetchResource(StructureDefinition.class, url); p = this.context.fetchResource(StructureDefinition.class, typeProfile);
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, profiles.get(0)); if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) {
} else { List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
elementValidated = true; validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension);
HashMap<String, List<ValidationMessage>> goodProfiles = new HashMap<String, List<ValidationMessage>>(); if (hasErrors(profileErrors))
HashMap<String, List<ValidationMessage>> badProfiles = new HashMap<String, List<ValidationMessage>>(); badProfiles.put(typeProfile, profileErrors);
for (String typeProfile : profiles) { else
String url = typeProfile; goodProfiles.put(typeProfile, profileErrors);
tail = null; }
if (url.contains("#")) { }
tail = url.substring(url.indexOf("#") + 1); if (goodProfiles.size() == 1) {
url = url.substring(0, url.indexOf("#")); errors.addAll(goodProfiles.values().iterator().next());
} } else if (goodProfiles.size() == 0) {
p = this.context.fetchResource(StructureDefinition.class, typeProfile); rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOMATCH, StringUtils.join("; ", profiles));
if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) { for (String m : badProfiles.keySet()) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>(); p = this.context.fetchResource(StructureDefinition.class, m);
validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); for (ValidationMessage message : badProfiles.get(m)) {
if (hasErrors(profileErrors)) message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])");
badProfiles.put(typeProfile, profileErrors); errors.add(message);
else
goodProfiles.put(typeProfile, profileErrors);
} }
} }
if (goodProfiles.size() == 1) { } else {
errors.addAll(goodProfiles.values().iterator().next()); warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MULTIPLEMATCHES, StringUtils.join("; ", goodProfiles.keySet()));
} else if (goodProfiles.size() == 0) { for (String m : goodProfiles.keySet()) {
rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOMATCH, StringUtils.join("; ", profiles)); p = this.context.fetchResource(StructureDefinition.class, m);
for (String m : badProfiles.keySet()) { for (ValidationMessage message : goodProfiles.get(m)) {
p = this.context.fetchResource(StructureDefinition.class, m); message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])");
for (ValidationMessage message : badProfiles.get(m)) { errors.add(message);
message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])");
errors.add(message);
}
}
} else {
warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MULTIPLEMATCHES, StringUtils.join("; ", goodProfiles.keySet()));
for (String m : goodProfiles.keySet()) {
p = this.context.fetchResource(StructureDefinition.class, m);
for (ValidationMessage message : goodProfiles.get(m)) {
message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])");
errors.add(message);
}
} }
} }
} }
if (p != null) { }
trackUsage(p, hostContext, element); if (p != null) {
trackUsage(p, hostContext, element);
if (!elementValidated) { if (!elementValidated) {
if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER)
validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension);
else else
validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension);
} }
int index = profile.getSnapshot().getElement().indexOf(ei.definition); int index = profile.getSnapshot().getElement().indexOf(checkDefn);
if (index < profile.getSnapshot().getElement().size() - 1) { if (index < profile.getSnapshot().getElement().size() - 1) {
String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath(); String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath();
if (!nextPath.equals(ei.definition.getPath()) && nextPath.startsWith(ei.definition.getPath())) if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath()))
validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension);
}
} }
} }
} }
@ -5091,4 +5115,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return questionnaireMode; return questionnaireMode;
} }
public boolean isWantCheckSnapshotUnchanged() {
return wantCheckSnapshotUnchanged;
}
public void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged) {
this.wantCheckSnapshotUnchanged = wantCheckSnapshotUnchanged;
}
} }