use of org.hl7.fhir.validation.instance.utils.NodeStack in project org.hl7.fhir.core by hapifhir.
the class InstanceValidator method validateElement.
private void validateElement(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl) throws FHIRException {
String id = element.getChildValue("id");
if (!Utilities.noString(id)) {
if (stack.getIds().containsKey(id) && stack.getIds().get(id) != element) {
rule(errors, IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.DUPLICATE_ID, id);
}
if (!stack.isResetPoint()) {
stack.getIds().put(id, element);
}
}
if (definition.getPath().equals("StructureDefinition.snapshot")) {
// work around a known issue in the spec, that ids are duplicated in snapshot and differential
stack.resetIds();
}
// check type invariants
checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false);
if (definition.getFixed() != null) {
checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null, false);
}
if (definition.getPattern() != null) {
checkFixedValue(errors, stack.getLiteralPath(), element, definition.getPattern(), profile.getUrl(), definition.getSliceName(), null, true);
}
// get the list of direct defined children, including slices
List<ElementDefinition> childDefinitions = profileUtilities.getChildMap(profile, definition);
if (childDefinitions.isEmpty()) {
if (actualType == null)
// there'll be an error elsewhere in this case, and we're going to stop.
return;
childDefinitions = getActualTypeChildren(hostContext, element, actualType);
} else if (definition.getType().size() > 1) {
// this only happens when the profile constrains the abstract children but leaves th choice open.
if (actualType == null)
// there'll be an error elsewhere in this case, and we're going to stop.
return;
List<ElementDefinition> 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)
mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType);
}
List<ElementInfo> children = listChildren(element, stack);
List<String> problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children);
checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths);
// 5. inspect each child for validity
for (ElementInfo ei : children) {
checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl);
}
}
use of org.hl7.fhir.validation.instance.utils.NodeStack in project org.hl7.fhir.core by hapifhir.
the class InstanceValidator method makeExternalRef.
private ResolvedReference makeExternalRef(Element external, String path) {
ResolvedReference res = new ResolvedReference();
res.setResource(external);
res.setFocus(external);
res.setExternal(true);
res.setStack(new NodeStack(context, external, path, validationLanguage));
return res;
}
use of org.hl7.fhir.validation.instance.utils.NodeStack in project org.hl7.fhir.core by hapifhir.
the class InstanceValidator method checkCardinalities.
public void checkCardinalities(List<ValidationMessage> errors, StructureDefinition profile, Element element, NodeStack stack, List<ElementDefinition> childDefinitions, List<ElementInfo> children, List<String> problematicPaths) throws DefinitionException {
// 3. report any definitions that have a cardinality problem
for (ElementDefinition ed : childDefinitions) {
if (ed.getRepresentation().isEmpty()) {
// ignore xml attributes
int count = 0;
List<ElementDefinition> slices = null;
if (ed.hasSlicing())
slices = profileUtilities.getSliceList(profile, ed);
for (ElementInfo ei : children) if (ei.definition == ed)
count++;
else if (slices != null) {
for (ElementDefinition sed : slices) {
if (ei.definition == sed) {
count++;
break;
}
}
}
if (ed.getMin() > 0) {
if (problematicPaths.contains(ed.getPath()))
hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(), ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()));
else {
if (count < ed.getMin()) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(), ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()), Integer.toString(count));
}
}
}
if (ed.hasMax() && !ed.getMax().equals("*")) {
if (problematicPaths.contains(ed.getPath()))
hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(), ed.getLabel(), stack.getLiteralPath(), ed.getMax());
else if (count > Integer.parseInt(ed.getMax())) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MAXIMUM, profile.getUrl(), ed.getPath(), ed.getId(), ed.getSliceName(), ed.getLabel(), stack.getLiteralPath(), ed.getMax(), Integer.toString(count));
}
}
}
}
}
use of org.hl7.fhir.validation.instance.utils.NodeStack in project org.hl7.fhir.core by hapifhir.
the class InstanceValidator method checkExtensionContext.
private boolean checkExtensionContext(List<ValidationMessage> errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext, boolean modifier) {
String extUrl = definition.getUrl();
boolean ok = false;
CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder();
List<String> plist = new ArrayList<>();
plist.add(stripIndexes(stack.getLiteralPath()));
for (String s : stack.getLogicalPaths()) {
String p = stripIndexes(s);
// all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition....
if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) {
return true;
}
plist.add(p);
}
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
if (ok) {
break;
}
if (ctxt.getType() == ExtensionContextType.ELEMENT) {
String en = ctxt.getExpression();
contexts.append("e:" + en);
if (Utilities.existsInList(en, "Element", "Any")) {
ok = true;
} else if (en.equals("Resource") && container.isResource()) {
ok = true;
}
for (String p : plist) {
if (ok) {
break;
}
if (p.equals(en)) {
ok = true;
} else {
String pn = p;
String pt = "";
if (p.contains(".")) {
pn = p.substring(0, p.indexOf("."));
pt = p.substring(p.indexOf("."));
}
StructureDefinition sd = context.fetchTypeDefinition(pn);
while (sd != null) {
if ((sd.getType() + pt).equals(en)) {
ok = true;
break;
}
if (sd.getBaseDefinition() != null) {
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
} else {
sd = null;
}
}
}
}
} else if (ctxt.getType() == ExtensionContextType.EXTENSION) {
contexts.append("x:" + ctxt.getExpression());
NodeStack estack = stack.getParent();
if (estack != null && estack.getElement().fhirType().equals("Extension")) {
String ext = estack.getElement().getNamedChildValue("url");
if (ctxt.getExpression().equals(ext)) {
ok = true;
}
}
} else if (ctxt.getType() == ExtensionContextType.FHIRPATH) {
contexts.append("p:" + ctxt.getExpression());
// The context is all elements that match the FHIRPath query found in the expression.
List<Base> res = fpe.evaluate(hostContext, resource, hostContext.getRootResource(), resource, fpe.parse(ctxt.getExpression()));
if (res.contains(container)) {
ok = true;
}
} else {
throw new Error(context.formatMessage(I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue()));
}
}
if (!ok) {
if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) {
warning(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString());
} else {
rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, modifier ? I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString());
}
return false;
} else {
if (definition.hasContextInvariant()) {
for (StringType s : definition.getContextInvariant()) {
if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) {
if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) {
warning(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, I18nConstants.PROFILE_EXT_NOT_HERE, extUrl, s.getValue());
return true;
} else {
rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, I18nConstants.PROFILE_EXT_NOT_HERE, extUrl, s.getValue());
return false;
}
}
}
}
return true;
}
}
use of org.hl7.fhir.validation.instance.utils.NodeStack in project org.hl7.fhir.core by hapifhir.
the class InstanceValidator method checkTerminologyCodeableConcept.
private boolean checkTerminologyCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) {
boolean res = true;
if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) {
if (binding.hasValueSet()) {
ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl());
if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(binding.getValueSet());
if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) {
warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()));
}
} else {
try {
CodeableConcept cc = convertToCodeableConcept(element, logical);
if (!cc.hasCoding()) {
if (binding.getStrength() == BindingStrength.REQUIRED)
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl());
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl());
else
warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeValueSet(binding.getValueSet()));
}
} else {
long t = System.nanoTime();
// Check whether the codes are appropriate for the type of binding we have
boolean bindingsOk = true;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path);
}
boolean atLeastOneSystemIsSupported = false;
for (Coding nextCoding : cc.getCoding()) {
String nextSystem = nextCoding.getSystem();
if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
atLeastOneSystemIsSupported = true;
break;
}
}
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter..
} else {
// we're going to validate the codings directly
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false);
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
if (binding.getStrength() == BindingStrength.REQUIRED)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString());
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
else if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString());
} else if (binding.getStrength() == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString());
}
}
} else {
if (binding.getStrength() == BindingStrength.REQUIRED)
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeValueSet(binding.getValueSet()), ccSummary(cc));
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeValueSet(binding.getValueSet()), ccSummary(cc));
} else if (binding.getStrength() == BindingStrength.PREFERRED) {
if (baseOnly) {
txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeValueSet(binding.getValueSet()), ccSummary(cc));
}
}
}
} else if (vr.getMessage() != null) {
res = false;
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
} else {
res = false;
}
}
// to validate, we'll validate that the codes actually exist
if (bindingsOk) {
for (Coding nextCoding : cc.getCoding()) {
String nextCode = nextCoding.getCode();
String nextSystem = nextCoding.getSystem();
String nextVersion = nextCoding.getVersion();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, nextVersion, null, false);
if (!vr.isOk()) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem);
}
}
}
}
timeTracker.tx(t, DataRenderer.display(context, cc));
}
}
} catch (Exception e) {
if (STACK_TRACE)
e.printStackTrace();
warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getMessage());
}
// special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding.
if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) {
checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical);
}
}
} else if (binding.hasValueSet()) {
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
} else if (!noBindingMsgSuppressed) {
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
}
}
}
return res;
}
Aggregations