fix profile matching for CDA logical models

This commit is contained in:
Grahame Grieve 2023-09-30 22:30:20 +10:00
parent 589d566c0a
commit c2aa221c98
11 changed files with 257 additions and 195 deletions

View File

@ -100,7 +100,7 @@ import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
import org.hl7.fhir.validation.cli.utils.SchemaValidator; import org.hl7.fhir.validation.cli.utils.SchemaValidator;
import org.hl7.fhir.validation.cli.utils.ValidationLevel; import org.hl7.fhir.validation.cli.utils.ValidationLevel;
import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.InstanceValidator;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.hl7.fhir.validation.instance.utils.ValidationContext;
import org.hl7.fhir.utilities.ByteProvider; import org.hl7.fhir.utilities.ByteProvider;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -796,7 +796,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
FHIRPathEngine fpe = this.getValidator(null).getFHIRPathEngine(); FHIRPathEngine fpe = this.getValidator(null).getFHIRPathEngine();
Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.getFocus().getBytes()), cnt.getCntType()); Element e = Manager.parseSingle(context, new ByteArrayInputStream(cnt.getFocus().getBytes()), cnt.getCntType());
ExpressionNode exp = fpe.parse(expression); ExpressionNode exp = fpe.parse(expression);
return fpe.evaluateToString(new ValidatorHostContext(context, e), e, e, e, exp); return fpe.evaluateToString(new ValidationContext(context, e), e, e, e, exp);
} }
public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException { public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException {

View File

@ -46,7 +46,6 @@ import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -208,14 +207,18 @@ import org.hl7.fhir.validation.instance.type.StructureMapValidator;
import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableDefn; import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableDefn;
import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableSet; import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableSet;
import org.hl7.fhir.validation.instance.type.ValueSetValidator; import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.validation.instance.utils.CanonicalResourceLookupResult;
import org.hl7.fhir.validation.instance.utils.CanonicalTypeSorter;
import org.hl7.fhir.validation.instance.utils.ChildIterator; import org.hl7.fhir.validation.instance.utils.ChildIterator;
import org.hl7.fhir.validation.instance.utils.ElementInfo; import org.hl7.fhir.validation.instance.utils.ElementInfo;
import org.hl7.fhir.validation.instance.utils.EnableWhenEvaluator;
import org.hl7.fhir.validation.instance.utils.FHIRPathExpressionFixer; import org.hl7.fhir.validation.instance.utils.FHIRPathExpressionFixer;
import org.hl7.fhir.validation.instance.utils.IndexedElement; import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ResolvedReference; import org.hl7.fhir.validation.instance.utils.ResolvedReference;
import org.hl7.fhir.validation.instance.utils.ResourceValidationTracker; import org.hl7.fhir.validation.instance.utils.ResourceValidationTracker;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.hl7.fhir.validation.instance.utils.StructureDefinitionSorterByUrl;
import org.hl7.fhir.validation.instance.utils.ValidationContext;
import org.w3c.dom.Document; import org.w3c.dom.Document;
/** /**
@ -236,38 +239,6 @@ import org.w3c.dom.Document;
public class InstanceValidator extends BaseValidator implements IResourceValidator { public class InstanceValidator extends BaseValidator implements IResourceValidator {
public class StructureDefinitionSorterByUrl implements Comparator<StructureDefinition> {
@Override
public int compare(StructureDefinition o1, StructureDefinition o2) {
return o1.getUrl().compareTo(o2.getUrl());
}
}
public class CanonicalTypeSorter implements Comparator<CanonicalType> {
@Override
public int compare(CanonicalType o1, CanonicalType o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
public class CanonicalResourceLookupResult {
private CanonicalResource resource;
private String error;
public CanonicalResourceLookupResult(CanonicalResource resource) {
this.resource = resource;
}
public CanonicalResourceLookupResult(String error) {
this.error = error;
}
}
private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list";
private static final String EXECUTION_ID = "validator.execution.id"; private static final String EXECUTION_ID = "validator.execution.id";
private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)"; private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)";
@ -302,7 +273,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override @Override
public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
ValidatorHostContext c = (ValidatorHostContext) appContext; ValidationContext c = (ValidationContext) appContext;
if (externalHostServices != null) if (externalHostServices != null)
return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext);
else else
@ -320,7 +291,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return null; return null;
} }
} }
ValidatorHostContext c = (ValidatorHostContext) appContext; ValidationContext c = (ValidationContext) appContext;
if (externalHostServices != null) if (externalHostServices != null)
return externalHostServices.resolveConstantType(c.getAppContext(), name); return externalHostServices.resolveConstantType(c.getAppContext(), name);
else else
@ -352,7 +323,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override @Override
public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException {
ValidatorHostContext c = (ValidatorHostContext) appContext; ValidationContext c = (ValidationContext) appContext;
if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) {
return (Base) refContext.getUserData("validator.bundle.resolution"); return (Base) refContext.getUserData("validator.bundle.resolution");
@ -397,7 +368,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override @Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
ValidatorHostContext ctxt = (ValidatorHostContext) appContext; ValidationContext ctxt = (ValidationContext) appContext;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null) { if (sd == null) {
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_, url)); throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_, url));
@ -409,7 +380,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
Element e = new ObjectConverter(context).convert((Resource) item); Element e = new ObjectConverter(context).convert((Resource) item);
setParents(e); setParents(e);
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null,
mode); mode);
} catch (IOException e1) { } catch (IOException e1) {
throw new FHIRException(e1); throw new FHIRException(e1);
@ -417,11 +388,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (item instanceof Element) { } else if (item instanceof Element) {
Element e = (Element) item; Element e = (Element) item;
if (e.getSpecial() == SpecialElement.CONTAINED) { if (e.getSpecial() == SpecialElement.CONTAINED) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode); self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
} else if (e.getSpecial() != null) { } else if (e.getSpecial() != null) {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode); self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
} else { } else {
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode); self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
} }
} else } else
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT)); throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
@ -441,7 +412,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override @Override
public ValueSet resolveValueSet(Object appContext, String url) { public ValueSet resolveValueSet(Object appContext, String url) {
ValidatorHostContext c = (ValidatorHostContext) appContext; ValidationContext c = (ValidationContext) appContext;
if (c.getProfile() != null && url.startsWith("#")) { if (c.getProfile() != null && url.startsWith("#")) {
for (Resource r : c.getProfile().getContained()) { for (Resource r : c.getProfile().getContained()) {
if (r.getId().equals(url.substring(1))) { if (r.getId().equals(url.substring(1))) {
@ -929,7 +900,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime(); long t = System.nanoTime();
NodeStack stack = new NodeStack(context, path, element, validationLanguage); NodeStack stack = new NodeStack(context, path, element, validationLanguage);
if (profiles == null || profiles.isEmpty()) { if (profiles == null || profiles.isEmpty()) {
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition)); validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition));
} else { } else {
int i = 0; int i = 0;
while (i < profiles.size()) { while (i < profiles.size()) {
@ -947,7 +918,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
i++; i++;
} }
for (StructureDefinition defn : profiles) { for (StructureDefinition defn : profiles) {
validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile)); validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile));
} }
} }
if (hintAboutNonMustSupport) { if (hintAboutNonMustSupport) {
@ -1992,7 +1963,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean checkExtension(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl, PercentageTracker pct, ValidationMode mode) throws FHIRException { private boolean checkExtension(ValidationContext valContext, List<ValidationMessage> errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl, PercentageTracker pct, ValidationMode mode) throws FHIRException {
boolean ok = true; boolean ok = true;
String url = element.getNamedChildValue("url"); String url = element.getNamedChildValue("url");
String u = url.contains("|") ? url.substring(0, url.indexOf("|")) : url; String u = url.contains("|") ? url.substring(0, url.indexOf("|")) : url;
@ -2040,7 +2011,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
if (ex != null) { if (ex != null) {
trackUsage(ex, hostContext, element); trackUsage(ex, valContext, element);
// check internal definitions are coherent // check internal definitions are coherent
if (isModifier) { if (isModifier) {
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY) && ok;
@ -2049,7 +2020,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
// 1. can this extension be used here? // 1. can this extension be used here?
ok = checkExtensionContext(hostContext.getAppContext(), errors, resource, container, ex, containerStack, hostContext, isModifier) && ok; ok = checkExtensionContext(valContext.getAppContext(), errors, resource, container, ex, containerStack, valContext, isModifier) && ok;
ok = checkDefinitionStatus(errors, element, path, ex, profile, context.formatMessage(I18nConstants.MSG_DEPENDS_ON_EXTENSION)) && ok; ok = checkDefinitionStatus(errors, element, path, ex, profile, context.formatMessage(I18nConstants.MSG_DEPENDS_ON_EXTENSION)) && ok;
if (isModifier) if (isModifier)
@ -2068,7 +2039,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), I18nConstants.EXTENSION_EXT_SIMPLE_ABSENT, url) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), I18nConstants.EXTENSION_EXT_SIMPLE_ABSENT, url) && ok;
// 3. is the content of the extension valid? // 3. is the content of the extension valid?
ok = validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true, url, pct, mode) && ok; ok = validateElement(valContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true, url, pct, mode) && ok;
} }
return ok; return ok;
@ -2119,7 +2090,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return res; return res;
} }
private boolean checkExtensionContext(Object appContext, List<ValidationMessage> errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext, boolean modifier) { private boolean checkExtensionContext(Object appContext, List<ValidationMessage> errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidationContext valContext, boolean modifier) {
String extUrl = definition.getUrl(); String extUrl = definition.getUrl();
boolean ok = false; boolean ok = false;
CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder(); CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder();
@ -2209,7 +2180,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) {
contexts.append("p:" + ctxt.getExpression()); contexts.append("p:" + ctxt.getExpression());
// The context is all elements that match the FHIRPath query found in the expression. // 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())); List<Base> res = fpe.evaluate(valContext, resource, valContext.getRootResource(), resource, fpe.parse(ctxt.getExpression()));
if (res.contains(container)) { if (res.contains(container)) {
ok = true; ok = true;
} }
@ -2229,7 +2200,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else { } else {
if (definition.hasContextInvariant()) { if (definition.hasContextInvariant()) {
for (StringType s : definition.getContextInvariant()) { for (StringType s : definition.getContextInvariant()) {
if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { if (!fpe.evaluateToBoolean(valContext, resource, valContext.getRootResource(), container, fpe.parse(s.getValue()))) {
if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) { if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) {
warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, I18nConstants.PROFILE_EXT_NOT_HERE, extUrl, s.getValue()); warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false, I18nConstants.PROFILE_EXT_NOT_HERE, extUrl, s.getValue());
return true; return true;
@ -2291,7 +2262,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (sd.getType().equals(resource.fhirType())) { } else if (sd.getType().equals(resource.fhirType())) {
List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>();
ValidationMode mode = new ValidationMode(ValidationReason.Expression, ProfileSource.FromExpression); ValidationMode mode = new ValidationMode(ValidationReason.Expression, ProfileSource.FromExpression);
validateResource(new ValidatorHostContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode); validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode);
boolean ok = true; boolean ok = true;
List<ValidationMessage> record = new ArrayList<>(); List<ValidationMessage> record = new ArrayList<>();
for (ValidationMessage v : valerrors) { for (ValidationMessage v : valerrors) {
@ -2517,7 +2488,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean checkPrimitive(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { private boolean checkPrimitive(ValidationContext valContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException {
boolean ok = true; boolean ok = true;
if (isBlank(e.primitiveValue())) { if (isBlank(e.primitiveValue())) {
if (e.primitiveValue() == null) if (e.primitiveValue() == null)
@ -2626,7 +2597,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, Utilities.isAbsoluteUrl(url), ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, Utilities.isAbsoluteUrl(url),
node.isContained() ? I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED : I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url) && ok; node.isContained() ? I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED : I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url) && ok;
} else if (!e.getProperty().getDefinition().getPath().equals("Bundle.entry.fullUrl")) { // we don't check fullUrl here; it's not a reference, it's a definition. It'll get checked as part of checking the bundle } else if (!e.getProperty().getDefinition().getPath().equals("Bundle.entry.fullUrl")) { // we don't check fullUrl here; it's not a reference, it's a definition. It'll get checked as part of checking the bundle
ok = validateReference(hostContext, errors, path, type, context, e, url) && ok; ok = validateReference(valContext, errors, path, type, context, e, url) && ok;
} }
} }
if (type.equals(ID) && !"Resource.id".equals(context.getBase().getPath())) { if (type.equals(ID) && !"Resource.id".equals(context.getBase().getPath())) {
@ -2799,7 +2770,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if ("StructureDefinition.type".equals(context.getPath()) && "http://hl7.org/fhir/StructureDefinition/StructureDefinition".equals(profile.getUrl())) { if ("StructureDefinition.type".equals(context.getPath()) && "http://hl7.org/fhir/StructureDefinition/StructureDefinition".equals(profile.getUrl())) {
ok = checkTypeValue(errors, path, e, node.getElement()); ok = checkTypeValue(errors, path, e, node.getElement());
} else { } else {
ok = checkPrimitiveBinding(hostContext, errors, path, type, context, e, profile, node) && ok; ok = checkPrimitiveBinding(valContext, errors, path, type, context, e, profile, node) && ok;
} }
} }
@ -2924,7 +2895,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return Utilities.escapeJson(s); return Utilities.escapeJson(s);
} }
public boolean validateReference(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, String url) { public boolean validateReference(ValidationContext valContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, String url) {
boolean ok = true; boolean ok = true;
// now, do we check the URI target? // now, do we check the URI target?
if (fetcher != null && !type.equals("uuid")) { if (fetcher != null && !type.equals("uuid")) {
@ -2933,14 +2904,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) /* || (url.startsWith("http://hl7.org/fhir/tools")) */ || found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) /* || (url.startsWith("http://hl7.org/fhir/tools")) */ ||
SpecialExtensions.isKnownExtension(url) || isXverUrl(url); SpecialExtensions.isKnownExtension(url) || isXverUrl(url);
if (!found) { if (!found) {
found = fetcher.resolveURL(this, hostContext, path, url, type, type.equals("canonical")); found = fetcher.resolveURL(this, valContext, path, url, type, type.equals("canonical"));
} }
} catch (IOException e1) { } catch (IOException e1) {
found = false; found = false;
} }
if (!found) { if (!found) {
if (type.equals("canonical")) { if (type.equals("canonical")) {
ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url); ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, valContext, path, url);
if (rp == ReferenceValidationPolicy.CHECK_EXISTS || rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE) { if (rp == ReferenceValidationPolicy.CHECK_EXISTS || rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url) && ok;
} else { } else {
@ -2957,12 +2928,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else { } else {
if (type.equals("canonical")) { if (type.equals("canonical")) {
ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url); ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, valContext, path, url);
if (rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE || rp == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS || rp == ReferenceValidationPolicy.CHECK_VALID) { if (rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE || rp == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS || rp == ReferenceValidationPolicy.CHECK_VALID) {
try { try {
Resource r = null; Resource r = null;
if (url.startsWith("#")) { if (url.startsWith("#")) {
r = loadContainedResource(errors, path, hostContext.getRootResource(), url.substring(1), Resource.class); r = loadContainedResource(errors, path, valContext.getRootResource(), url.substring(1), Resource.class);
} }
if (r == null) { if (r == null) {
r = fetcher.fetchCanonicalResource(this, url); r = fetcher.fetchCanonicalResource(this, url);
@ -3243,7 +3214,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean checkPrimitiveBinding(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { private boolean checkPrimitiveBinding(ValidationContext valContext, List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) {
// We ignore bindings that aren't on string, uri or code // We ignore bindings that aren't on string, uri or code
if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) {
return true; return true;
@ -3268,7 +3239,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else { } else {
CodedContentValidationPolicy validationPolicy = getPolicyAdvisor() == null ? CodedContentValidationPolicy validationPolicy = getPolicyAdvisor() == null ?
CodedContentValidationPolicy.VALUESET : getPolicyAdvisor().policyForCodedContent(this, hostContext, stack.getLiteralPath(), elementContext, profile, BindingKind.PRIMARY, vs, new ArrayList<>()); CodedContentValidationPolicy.VALUESET : getPolicyAdvisor().policyForCodedContent(this, valContext, stack.getLiteralPath(), elementContext, profile, BindingKind.PRIMARY, vs, new ArrayList<>());
if (validationPolicy != CodedContentValidationPolicy.IGNORE) { if (validationPolicy != CodedContentValidationPolicy.IGNORE) {
long t = System.nanoTime(); long t = System.nanoTime();
@ -3563,7 +3534,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean checkReference(ValidatorHostContext hostContext, private boolean checkReference(ValidationContext valContext,
List<ValidationMessage> errors, List<ValidationMessage> errors,
String path, String path,
Element element, Element element,
@ -3590,7 +3561,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref); warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref);
BooleanHolder bh = new BooleanHolder(); BooleanHolder bh = new BooleanHolder();
ResolvedReference we = localResolve(ref, stack, errors, path, hostContext.getRootResource(), hostContext.getGroupingResource(), element, bh); ResolvedReference we = localResolve(ref, stack, errors, path, valContext.getRootResource(), valContext.getGroupingResource(), element, bh);
ok = bh.ok() && ok; ok = bh.ok() && ok;
String refType; String refType;
if (ref.startsWith("#")) { if (ref.startsWith("#")) {
@ -3610,7 +3581,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (policyAdvisor == null) { if (policyAdvisor == null) {
pol = ReferenceValidationPolicy.IGNORE; pol = ReferenceValidationPolicy.IGNORE;
} else { } else {
pol = policyAdvisor.policyForReference(this, hostContext.getAppContext(), path, ref); pol = policyAdvisor.policyForReference(this, valContext.getAppContext(), path, ref);
} }
} }
@ -3631,7 +3602,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ext = fetchCache.get(ref); ext = fetchCache.get(ref);
} else { } else {
try { try {
ext = fetcher.fetch(this, hostContext.getAppContext(), ref); ext = fetcher.fetch(this, valContext.getAppContext(), ref);
} catch (IOException e) { } catch (IOException e) {
if (STACK_TRACE) e.printStackTrace(); if (STACK_TRACE) e.printStackTrace();
throw new FHIRException(e); throw new FHIRException(e);
@ -3718,12 +3689,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
int goodCount = 0; int goodCount = 0;
for (StructureDefinition pr : profiles) { for (StructureDefinition pr : profiles) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, validateResource(we.valContext(valContext, pr), profileErrors, we.getResource(), we.getFocus(), pr,
IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice)); IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice));
if (!hasErrors(profileErrors)) { if (!hasErrors(profileErrors)) {
goodCount++; goodCount++;
goodProfiles.put(pr, profileErrors); goodProfiles.put(pr, profileErrors);
trackUsage(pr, hostContext, element); trackUsage(pr, valContext, element);
} else { } else {
badProfiles.put(pr, profileErrors); badProfiles.put(pr, profileErrors);
} }
@ -4642,7 +4613,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
* @throws IOException * @throws IOException
* @throws FHIRException * @throws FHIRException
*/ */
private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, NodeStack stack, StructureDefinition srcProfile) throws DefinitionException, FHIRException { private boolean sliceMatches(ValidationContext valContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, NodeStack stack, StructureDefinition srcProfile) throws DefinitionException, FHIRException {
if (!slicer.getSlicing().hasDiscriminator()) if (!slicer.getSlicing().hasDiscriminator())
return false; // cannot validate in this case return false; // cannot validate in this case
@ -4746,7 +4717,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else { } else {
} }
ValidatorHostContext shc = hostContext.forSlicing(); ValidationContext shc = valContext.forSlicing();
boolean pass = evaluateSlicingExpression(shc, element, path, profile, n); boolean pass = evaluateSlicingExpression(shc, element, path, profile, n);
if (!pass) { if (!pass) {
slicingHint(sliceInfo, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer), (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName(), n.toString().substring(8).trim())), "discriminator = " + Utilities.escapeXml(n.toString()), null); slicingHint(sliceInfo, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer), (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName(), n.toString().substring(8).trim())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
@ -4784,12 +4755,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return false; return false;
} }
public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException { public boolean evaluateSlicingExpression(ValidationContext valContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException {
String msg; String msg;
boolean pass; boolean pass;
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
pass = fpe.evaluateToBoolean(hostContext.forProfile(profile), hostContext.getResource(), hostContext.getRootResource(), element, n); pass = fpe.evaluateToBoolean(valContext.forProfile(profile), valContext.getResource(), valContext.getRootResource(), element, n);
timeTracker.fpe(t); timeTracker.fpe(t);
msg = fpe.forLog(); msg = fpe.forLog();
} catch (Exception ex) { } catch (Exception ex) {
@ -5079,7 +5050,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
// checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up)
private boolean start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException { private boolean start(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException {
boolean ok = !hasErrors(errors); boolean ok = !hasErrors(errors);
checkLang(resource, stack); checkLang(resource, stack);
@ -5099,7 +5070,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
resolveBundleReferences(element, new ArrayList<Element>()); resolveBundleReferences(element, new ArrayList<Element>());
} }
ok = startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials(), pct, mode) && ok; ok = startInner(valContext, errors, resource, element, defn, stack, valContext.isCheckSpecials(), pct, mode) && ok;
if (pctOwned) { if (pctOwned) {
pct.done(); pct.done();
} }
@ -5118,7 +5089,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (pctOwned) { if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress); pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress);
} }
ok = startInner(hostContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok; ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok;
if (pctOwned) { if (pctOwned) {
pct.done(); pct.done();
} }
@ -5166,7 +5137,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (pctOwned) { if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl(), logProgress); pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl(), logProgress);
} }
ok = startInner(hostContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile)) && ok; ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile)) && ok;
if (pctOwned) { if (pctOwned) {
pct.done(); pct.done();
} }
@ -5183,7 +5154,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (pctOwned) { if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress); pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress);
} }
ok = startInner(hostContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok; ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok;
if (pctOwned) { if (pctOwned) {
pct.done(); pct.done();
} }
@ -5210,7 +5181,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (pctOwned) { if (pctOwned) {
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getVersionedUrl(), logProgress); pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getVersionedUrl(), logProgress);
} }
ok = startInner(hostContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile)) && ok; ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile)) && ok;
if (pctOwned) { if (pctOwned) {
pct.done(); pct.done();
} }
@ -5227,10 +5198,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String url = profile.primitiveValue(); String url = profile.primitiveValue();
CanonicalResourceLookupResult cr = crLookups.get(url); CanonicalResourceLookupResult cr = crLookups.get(url);
if (cr != null) { if (cr != null) {
if (cr.error != null) { if (cr.getError() != null) {
warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, url, cr.error); warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", false, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN_ERROR, url, cr.getError());
} else { } else {
sd = (StructureDefinition) cr.resource; sd = (StructureDefinition) cr.getResource();
} }
} else { } else {
try { try {
@ -5311,7 +5282,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
public boolean startInner(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) { public boolean startInner(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
// the first piece of business is to see if we've validated this resource against this profile before. // the first piece of business is to see if we've validated this resource against this profile before.
// if we have (*or if we still are*), then we'll just return our existing errors // if we have (*or if we still are*), then we'll just return our existing errors
boolean ok = true; boolean ok = true;
@ -5329,8 +5300,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(), I18nConstants.VALIDATION_VAL_PROFILE_NOSNAPSHOT, defn.getVersionedUrl())) { if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(), I18nConstants.VALIDATION_VAL_PROFILE_NOSNAPSHOT, defn.getVersionedUrl())) {
List<ValidationMessage> localErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> localErrors = new ArrayList<ValidationMessage>();
resTracker.startValidating(defn); resTracker.startValidating(defn);
trackUsage(defn, hostContext, element); trackUsage(defn, valContext, element);
ok = validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null, pct, mode) && ok; ok = validateElement(valContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null, pct, mode) && ok;
resTracker.storeOutcomes(defn, localErrors); resTracker.storeOutcomes(defn, localErrors);
for (ValidationMessage vm : localErrors) { for (ValidationMessage vm : localErrors) {
if (!errors.contains(vm)) { if (!errors.contains(vm)) {
@ -5341,13 +5312,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = false; ok = false;
} }
if (checkSpecials) { if (checkSpecials) {
ok = checkSpecials(hostContext, errors, element, stack, checkSpecials, pct, mode) && ok; ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode) && ok;
ok = validateResourceRules(errors, element, stack) && ok; ok = validateResourceRules(errors, element, stack) && ok;
} }
return ok; return ok;
} }
public boolean checkSpecials(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) { public boolean checkSpecials(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
boolean ok = true; boolean ok = true;
long t = System.nanoTime(); long t = System.nanoTime();
@ -5367,17 +5338,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
if (element.getType().equals(BUNDLE)) { if (element.getType().equals(BUNDLE)) {
return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode) && ok; return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, valContext, pct, mode) && ok;
} else if (element.getType().equals("Observation")) { } else if (element.getType().equals("Observation")) {
return validateObservation(errors, element, stack) && ok; return validateObservation(errors, element, stack) && ok;
} else if (element.getType().equals("Questionnaire")) { } else if (element.getType().equals("Questionnaire")) {
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaire(errors, element, element, stack) && ok; return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaire(errors, element, element, stack) && ok;
} else if (element.getType().equals("QuestionnaireResponse")) { } else if (element.getType().equals("QuestionnaireResponse")) {
return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaireResponse(hostContext, errors, element, stack) && ok; return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaireResponse(valContext, errors, element, stack) && ok;
} else if (element.getType().equals("Measure")) { } else if (element.getType().equals("Measure")) {
return new MeasureValidator(this).validateMeasure(hostContext, errors, element, stack) && ok; return new MeasureValidator(this).validateMeasure(valContext, errors, element, stack) && ok;
} else if (element.getType().equals("MeasureReport")) { } else if (element.getType().equals("MeasureReport")) {
return new MeasureValidator(this).validateMeasureReport(hostContext, errors, element, stack) && ok; return new MeasureValidator(this).validateMeasureReport(valContext, errors, element, stack) && ok;
} else if (element.getType().equals("CapabilityStatement")) { } else if (element.getType().equals("CapabilityStatement")) {
return validateCapabilityStatement(errors, element, stack) && ok; return validateCapabilityStatement(errors, element, stack) && ok;
} else if (element.getType().equals("CodeSystem")) { } else if (element.getType().equals("CodeSystem")) {
@ -5538,7 +5509,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean validateContains(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, private boolean validateContains(ValidationContext valContext, List<ValidationMessage> errors, String path,
ElementDefinition child, ElementDefinition context, Element resource, ElementDefinition child, ElementDefinition context, Element resource,
Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct, ValidationMode mode) throws FHIRException { Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile, PercentageTracker pct, ValidationMode mode) throws FHIRException {
boolean ok = true; boolean ok = true;
@ -5556,7 +5527,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ?
ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this,
hostContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl()); valContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl());
if (containedValidationPolicy.ignore()) { if (containedValidationPolicy.ignore()) {
return ok; return ok;
@ -5583,13 +5554,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (isValidResourceType(resourceName, typeForResource)) { } else if (isValidResourceType(resourceName, typeForResource)) {
if (containedValidationPolicy.checkValid()) { if (containedValidationPolicy.checkValid()) {
// special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise
ValidatorHostContext hc = null; ValidationContext hc = null;
if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) { if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) {
resource = element; resource = element;
assert Utilities.existsInList(hostContext.getResource().fhirType(), "Bundle", "Parameters") : "Containing Resource is "+hostContext.getResource().fhirType()+", expected Bundle or Parameters at "+stack.getLiteralPath(); assert Utilities.existsInList(valContext.getResource().fhirType(), "Bundle", "Parameters") : "Containing Resource is "+valContext.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) hc = valContext.forEntry(element, valContext.getResource()); // root becomes the grouping resource (should be either bundle or parameters)
} else { } else {
hc = hostContext.forContained(element); hc = valContext.forContained(element);
} }
stack.resetIds(); stack.resetIds();
@ -5609,13 +5580,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
checkSpecials(hostContext, errors, element, stack, ok, pct, mode); checkSpecials(valContext, errors, element, stack, ok, pct, mode);
if (typeForResource.getProfile().size() == 1) { if (typeForResource.getProfile().size() == 1) {
long t = System.nanoTime(); long t = System.nanoTime();
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue(), parentProfile); StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue(), parentProfile);
timeTracker.sd(t); timeTracker.sd(t);
trackUsage(profile, hostContext, element); trackUsage(profile, valContext, element);
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) { profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) {
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok; ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
@ -5627,7 +5598,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, StructureDefinition profile = this.context.fetchResource(StructureDefinition.class,
"http://hl7.org/fhir/StructureDefinition/" + resourceName); "http://hl7.org/fhir/StructureDefinition/" + resourceName);
timeTracker.sd(t); timeTracker.sd(t);
trackUsage(profile, hostContext, element); trackUsage(profile, valContext, element);
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) { profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) {
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok; ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
@ -5648,7 +5619,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.sd(t); timeTracker.sd(t);
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), u.asStringValue())) { profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), u.asStringValue())) {
trackUsage(profile, hostContext, element); trackUsage(profile, valContext, element);
List<ValidationMessage> perrors = new ArrayList<>(); List<ValidationMessage> perrors = new ArrayList<>();
errorsList.add(perrors); errorsList.add(perrors);
if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode)) { if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode)) {
@ -5735,7 +5706,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
private boolean validateElement(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context, private boolean validateElement(ValidationContext valContext, 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, PercentageTracker pct, ValidationMode mode) throws FHIRException { Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl, PercentageTracker pct, ValidationMode mode) throws FHIRException {
boolean ok = true; boolean ok = true;
@ -5757,7 +5728,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidationInfo vi = element.addDefinition(profile, definition, mode); ValidationInfo vi = element.addDefinition(profile, definition, mode);
// check type invariants // check type invariants
ok = checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false) & ok; ok = checkInvariants(valContext, errors, profile, definition, resource, element, stack, false) & ok;
if (definition.getFixed() != null) { if (definition.getFixed() != null) {
ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getVersionedUrl(), definition.getSliceName(), null, false) && ok; ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getVersionedUrl(), definition.getSliceName(), null, false) && ok;
} }
@ -5772,21 +5743,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
vi.setValid(false); vi.setValid(false);
return false; // there'll be an error elsewhere in this case, and we're going to stop. return false; // there'll be an error elsewhere in this case, and we're going to stop.
} }
childDefinitions = getActualTypeChildren(hostContext, element, actualType); childDefinitions = getActualTypeChildren(valContext, element, actualType);
} else if (definition.getType().size() > 1) { } else if (definition.getType().size() > 1) {
// this only happens when the profile constrains the abstract children but leaves th choice open. // this only happens when the profile constrains the abstract children but leaves th choice open.
if (actualType == null) { if (actualType == null) {
vi.setValid(false); vi.setValid(false);
return false; // there'll be an error elsewhere in this case, and we're going to stop. return false; // there'll be an error elsewhere in this case, and we're going to stop.
} }
SourcedChildDefinitions typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); SourcedChildDefinitions typeChildDefinitions = getActualTypeChildren(valContext, element, actualType);
// what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored)
childDefinitions = mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); childDefinitions = mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType);
} }
List<ElementInfo> children = listChildren(element, stack); List<ElementInfo> children = listChildren(element, stack);
BooleanHolder bh = new BooleanHolder(); BooleanHolder bh = new BooleanHolder();
List<String> problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children, bh); List<String> problematicPaths = assignChildren(valContext, errors, profile, resource, stack, childDefinitions, children, bh);
ok = bh.ok() && ok; ok = bh.ok() && ok;
ok = checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths) && ok; ok = checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths) && ok;
@ -5794,7 +5765,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// 5. inspect each child for validity // 5. inspect each child for validity
for (ElementInfo ei : children) { for (ElementInfo ei : children) {
ok = checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, pct, mode) && ok; ok = checkChild(valContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, pct, mode) && ok;
} }
vi.setValid(ok); vi.setValid(ok);
return ok; return ok;
@ -5819,7 +5790,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
// todo: the element definition in context might assign a constrained profile for the type? // todo: the element definition in context might assign a constrained profile for the type?
public SourcedChildDefinitions getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { public SourcedChildDefinitions getActualTypeChildren(ValidationContext valContext, Element element, String actualType) {
SourcedChildDefinitions childDefinitions; SourcedChildDefinitions childDefinitions;
StructureDefinition dt = null; StructureDefinition dt = null;
if (isAbsolute(actualType)) if (isAbsolute(actualType))
@ -5828,13 +5799,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType);
if (dt == null) if (dt == null)
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType)); throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType));
trackUsage(dt, hostContext, element); trackUsage(dt, valContext, element);
childDefinitions = profileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); childDefinitions = profileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0));
return childDefinitions; return childDefinitions;
} }
public boolean checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, public boolean checkChild(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition,
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, PercentageTracker pct, ValidationMode mode) Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, PercentageTracker pct, ValidationMode mode)
throws FHIRException, DefinitionException { throws FHIRException, DefinitionException {
boolean ok = true; boolean ok = true;
@ -5846,13 +5817,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (debug) { if (debug) {
System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against defn "+ei.definition.getId()+" from "+profile.getVersionedUrl()+time()); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against defn "+ei.definition.getId()+" from "+profile.getVersionedUrl()+time());
} }
ok = checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.definition, false, pct, mode) && ok; ok = checkChildByDefinition(valContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.definition, false, pct, mode) && ok;
} }
if (ei.slice != null) { if (ei.slice != null) {
if (debug) { if (debug) {
System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against slice "+ei.slice.getId()+time()); System.out.println(Utilities.padLeft("", ' ', stack.depth())+ "Check "+ei.getPath()+" against slice "+ei.slice.getId()+time());
} }
ok = checkChildByDefinition(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.slice, true, pct, mode) && ok; ok = checkChildByDefinition(valContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl, ei.slice, true, pct, mode) && ok;
} }
return ok; return ok;
} }
@ -5864,7 +5835,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return s; return s;
} }
public boolean checkChildByDefinition(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, public boolean checkChildByDefinition(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile,
ElementDefinition definition, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, ElementDefinition definition, Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept,
boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, ElementDefinition checkDefn, boolean isSlice, PercentageTracker pct, ValidationMode mode) { boolean checkDisplayInContext, ElementInfo ei, String extensionUrl, ElementDefinition checkDefn, boolean isSlice, PercentageTracker pct, ValidationMode mode) {
boolean ok = true; boolean ok = true;
@ -5980,16 +5951,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
SpecialElement special = ei.getElement().getSpecial(); SpecialElement special = ei.getElement().getSpecial();
// this used to say // this used to say
// if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) { // if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) {
// ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok; // ok = checkInvariants(valContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok;
// but this isn't correct - when the invariant is on the element, the invariant is in the context of the resource that contains the element. // but this isn't correct - when the invariant is on the element, the invariant is in the context of the resource that contains the element.
// changed 18-Jul 2023 - see https://chat.fhir.org/#narrow/stream/179266-fhirpath/topic/FHIRPath.20.25resource.20variable // changed 18-Jul 2023 - see https://chat.fhir.org/#narrow/stream/179266-fhirpath/topic/FHIRPath.20.25resource.20variable
ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok; ok = checkInvariants(valContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok;
ei.getElement().markValidation(profile, checkDefn); ei.getElement().markValidation(profile, checkDefn);
boolean elementValidated = false; boolean elementValidated = false;
if (type != null) { if (type != null) {
if (isPrimitiveType(type)) { if (isPrimitiveType(type)) {
ok = checkPrimitive(hostContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, stack) && ok; ok = checkPrimitive(valContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, stack) && ok;
} else { } else {
if (checkDefn.hasFixed()) { if (checkDefn.hasFixed()) {
ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, false) && ok; ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, false) && ok;
@ -6012,7 +5983,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = bh.ok() & ok; ok = bh.ok() & ok;
thisIsCodeableConcept = true; thisIsCodeableConcept = true;
} else if (type.equals("Reference")) { } else if (type.equals("Reference")) {
ok = checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, checkDefn, actualType, localStack, pct, mode) && ok; ok = checkReference(valContext, errors, ei.getPath(), ei.getElement(), profile, checkDefn, actualType, localStack, pct, mode) && ok;
// 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 // 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")) { } else if (type.equals("Extension")) {
Element eurl = ei.getElement().getNamedChild("url"); Element eurl = ei.getElement().getNamedChild("url");
@ -6021,7 +5992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
thisExtension = url; thisExtension = url;
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) { if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) {
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) { if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) {
ok = checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), checkDefn, profile, localStack, stack, extensionUrl, pct, mode) && ok; ok = checkExtension(valContext, errors, ei.getPath(), resource, element, ei.getElement(), checkDefn, profile, localStack, stack, extensionUrl, pct, mode) && ok;
} else { } else {
ok = false; ok = false;
} }
@ -6032,7 +6003,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = false; ok = false;
} }
} else if (type.equals("Resource") || isResource(type)) { } else if (type.equals("Resource") || isResource(type)) {
ok = validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(), ok = validateContains(valContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(),
localStack, idStatusForEntry(element, ei), profile, pct, mode) && ok; // if localStack, idStatusForEntry(element, ei), profile, pct, mode) && ok; // if
elementValidated = true; elementValidated = true;
// (str.matches(".*([.,/])work\\1$")) // (str.matches(".*([.,/])work\\1$"))
@ -6050,7 +6021,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else { } else {
if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), checkDefn != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName())) { if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), checkDefn != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName())) {
ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, false, true, null, pct, mode) && ok; ok = validateElement(valContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, false, true, null, pct, mode) && ok;
} else { } else {
ok = false; ok = false;
} }
@ -6065,7 +6036,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// the invariants (constraints) on the current element, because otherwise it only gets // the invariants (constraints) on the current element, because otherwise it only gets
// checked against the primary type's invariants: LLoyd // checked against the primary type's invariants: LLoyd
//if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { //if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
// checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); // checkInvariants(valContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element);
//} //}
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_NOTYPE, type) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_NOTYPE, type) && ok;
@ -6092,7 +6063,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
p = this.context.fetchResource(StructureDefinition.class, typeProfile); p = this.context.fetchResource(StructureDefinition.class, typeProfile);
if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) { if (rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) {
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>(); List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode); // we don't track ok here validateElement(valContext, profileErrors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode); // we don't track ok here
if (hasErrors(profileErrors)) if (hasErrors(profileErrors))
badProfiles.put(typeProfile, profileErrors); badProfiles.put(typeProfile, profileErrors);
else else
@ -6124,24 +6095,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
if (p != null) { if (p != null) {
trackUsage(p, hostContext, element); trackUsage(p, valContext, 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)
ok = validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; ok = validateElement(valContext, errors, p, getElementByTail(p, tail), profile, checkDefn, ei.getElement(), ei.getElement(), type, localStack.resetIds(), thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
else else
ok = validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; ok = validateElement(valContext, errors, p, getElementByTail(p, tail), profile, checkDefn, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} }
int index = profile.getSnapshot().getElement().indexOf(checkDefn); 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(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) { if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) {
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) {
ok = validateElement(hostContext.forEntry(ei.getElement(), null), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; ok = validateElement(valContext.forEntry(ei.getElement(), null), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} else if (ei.getElement().getSpecial() == SpecialElement.CONTAINED) { } else if (ei.getElement().getSpecial() == SpecialElement.CONTAINED) {
ok = validateElement(hostContext.forContained(ei.getElement()), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; ok = validateElement(valContext.forContained(ei.getElement()), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} else { } else {
ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; ok = validateElement(valContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} }
} }
} }
@ -6171,9 +6142,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return sd != null && sd.getKind().equals(StructureDefinitionKind.RESOURCE); return sd != null && sd.getKind().equals(StructureDefinitionKind.RESOURCE);
} }
private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { private void trackUsage(StructureDefinition profile, ValidationContext valContext, Element element) {
if (tracker != null) { if (tracker != null) {
tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); tracker.recordProfileUsage(profile, valContext.getAppContext(), element);
} }
} }
@ -6278,7 +6249,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
public List<String> assignChildren(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource, public List<String> assignChildren(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource,
NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException { NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException {
// 2. assign children to a definition // 2. assign children to a definition
// for each definition, for each child, check whether it belongs in the slice // for each definition, for each child, check whether it belongs in the slice
@ -6317,7 +6288,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (ei.sliceInfo == null) { if (ei.sliceInfo == null) {
ei.sliceInfo = new ArrayList<>(); ei.sliceInfo = new ArrayList<>();
} }
unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei, bh); unsupportedSlicing = matchSlice(valContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei, bh);
} }
} }
int last = -1; int last = -1;
@ -6389,11 +6360,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return children; return children;
} }
public boolean checkInvariants(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException { public boolean checkInvariants(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException {
return checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); return checkInvariants(valContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited);
} }
public boolean matchSlice(ValidatorHostContext hostContext, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, StructureDefinition profile, NodeStack stack, public boolean matchSlice(ValidationContext valContext, List<ValidationMessage> errors, List<ValidationMessage> sliceInfo, StructureDefinition profile, NodeStack stack,
ElementDefinition slicer, boolean unsupportedSlicing, List<String> problematicPaths, int sliceOffset, int i, ElementDefinition ed, ElementDefinition slicer, boolean unsupportedSlicing, List<String> problematicPaths, int sliceOffset, int i, ElementDefinition ed,
boolean childUnsupportedSlicing, ElementInfo ei, BooleanHolder bh) { boolean childUnsupportedSlicing, ElementInfo ei, BooleanHolder bh) {
boolean match = false; boolean match = false;
@ -6403,7 +6374,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (nameMatches(ei.getName(), tail(ed.getPath()))) if (nameMatches(ei.getName(), tail(ed.getPath())))
try { try {
// System.out.println("match slices for "+stack.getLiteralPath()+": "+slicer.getId()+" = "+slicingSummary(slicer.getSlicing())); // System.out.println("match slices for "+stack.getLiteralPath()+": "+slicer.getId()+" = "+slicingSummary(slicer.getSlicing()));
match = sliceMatches(hostContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack, profile); match = sliceMatches(valContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack, profile);
if (match) { if (match) {
ei.slice = slicer; ei.slice = slicer;
@ -6522,7 +6493,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return IdStatus.REQUIRED; return IdStatus.REQUIRED;
} }
private boolean checkInvariants(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException { private boolean checkInvariants(ValidationContext valContext, List<ValidationMessage> errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException {
if (noInvariantChecks) { if (noInvariantChecks) {
return true; return true;
} }
@ -6543,7 +6514,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!invMap.keySet().contains(key)) { if (!invMap.keySet().contains(key)) {
invErrors = new ArrayList<ValidationMessage>(); invErrors = new ArrayList<ValidationMessage>();
invMap.put(key, invErrors); invMap.put(key, invErrors);
ok = checkInvariant(hostContext, invErrors, path, profile, resource, element, inv) && ok; ok = checkInvariant(valContext, invErrors, path, profile, resource, element, inv) && ok;
} else { } else {
invErrors = (ArrayList<ValidationMessage>)invMap.get(key); invErrors = (ArrayList<ValidationMessage>)invMap.get(key);
} }
@ -6585,7 +6556,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return false; return false;
} }
public boolean checkInvariant(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { public boolean checkInvariant(ValidationContext valContext, List<ValidationMessage> errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException {
if (IsExemptInvariant(path, element, inv)) { if (IsExemptInvariant(path, element, inv)) {
return true; return true;
} }
@ -6611,7 +6582,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String msg; String msg;
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
invOK = fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), element, n); invOK = fpe.evaluateToBoolean(valContext, resource, valContext.getRootResource(), element, n);
timeTracker.fpe(t); timeTracker.fpe(t);
msg = fpe.forLog(); msg = fpe.forLog();
} catch (Exception ex) { } catch (Exception ex) {
@ -6677,7 +6648,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
/* /*
* The actual base entry point for internal use (re-entrant) * The actual base entry point for internal use (re-entrant)
*/ */
private boolean validateResource(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, private boolean validateResource(ValidationContext valContext, List<ValidationMessage> errors, Element resource,
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException { Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException {
boolean ok = true; boolean ok = true;
@ -6728,9 +6699,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
// validate // validate
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), resourceName.equals(defn.getType()) || resourceName.equals(defn.getTypeTail()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), checkResourceName(defn, resourceName, element.getFormat()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE,
defn.getType(), resourceName, defn.getVersionedUrl())) { defn.getType(), resourceName, defn.getVersionedUrl())) {
ok = start(hostContext, errors, element, element, defn, stack, pct, mode) && ok; // root is both definition and type ok = start(valContext, errors, element, element, defn, stack, pct, mode) && ok; // root is both definition and type
} else { } else {
ok = false; ok = false;
} }
@ -6741,6 +6712,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private boolean checkResourceName(StructureDefinition defn, String resourceName, FhirFormat format) {
if (resourceName.equals(defn.getType())) {
return true;
}
if (resourceName.equals(defn.getTypeTail())) {
return true;
}
if (format == FhirFormat.XML) {
String xn = ToolingExtensions.readStringExtension(defn, "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name");
if (resourceName.equals(xn)) {
return true;
}
}
return false;
}
private String errorIds(String path, boolean ok, List<ValidationMessage> errors) { private String errorIds(String path, boolean ok, List<ValidationMessage> errors) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (ValidationMessage vm : errors) { for (ValidationMessage vm : errors) {

View File

@ -28,7 +28,7 @@ import org.hl7.fhir.validation.instance.PercentageTracker;
import org.hl7.fhir.validation.instance.utils.EntrySummary; import org.hl7.fhir.validation.instance.utils.EntrySummary;
import org.hl7.fhir.validation.instance.utils.IndexedElement; import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.hl7.fhir.validation.instance.utils.ValidationContext;
public class BundleValidator extends BaseValidator { public class BundleValidator extends BaseValidator {
public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?";
@ -39,7 +39,7 @@ public class BundleValidator extends BaseValidator {
this.serverBase = serverBase; this.serverBase = serverBase;
} }
public boolean validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext, PercentageTracker pct, ValidationMode mode) { public boolean validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidationContext hostContext, PercentageTracker pct, ValidationMode mode) {
boolean ok = true; boolean ok = true;
String type = bundle.getNamedChildValue(TYPE); String type = bundle.getNamedChildValue(TYPE);
type = StringUtils.defaultString(type); type = StringUtils.defaultString(type);

View File

@ -33,7 +33,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.xml.XMLUtil; import org.hl7.fhir.utilities.xml.XMLUtil;
import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.hl7.fhir.validation.instance.utils.ValidationContext;
import org.w3c.dom.Document; import org.w3c.dom.Document;
public class MeasureValidator extends BaseValidator { public class MeasureValidator extends BaseValidator {
@ -43,7 +43,7 @@ public class MeasureValidator extends BaseValidator {
super(parent); super(parent);
} }
public boolean validateMeasure(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException { public boolean validateMeasure(ValidationContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException {
boolean ok = true; boolean ok = true;
MeasureContext mctxt = new MeasureContext(); MeasureContext mctxt = new MeasureContext();
List<Element> libs = element.getChildrenByName("library"); List<Element> libs = element.getChildrenByName("library");
@ -131,7 +131,7 @@ public class MeasureValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateMeasureCriteria(ValidatorHostContext hostContext, List<ValidationMessage> errors, MeasureContext mctxt, Element crit, NodeStack nsc) { private boolean validateMeasureCriteria(ValidationContext hostContext, List<ValidationMessage> errors, MeasureContext mctxt, Element crit, NodeStack nsc) {
boolean ok = true; boolean ok = true;
String mimeType = crit.getChildValue("language"); String mimeType = crit.getChildValue("language");
if (!Utilities.noString(mimeType)) { // that would be an error elsewhere if (!Utilities.noString(mimeType)) { // that would be an error elsewhere
@ -206,7 +206,7 @@ public class MeasureValidator extends BaseValidator {
// --------------------------------------------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------------------------------------------
public boolean validateMeasureReport(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException { public boolean validateMeasureReport(ValidationContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException {
boolean ok = true; boolean ok = true;
Element m = element.getNamedChild("measure"); Element m = element.getNamedChild("measure");
String measure = null; String measure = null;
@ -282,7 +282,7 @@ public class MeasureValidator extends BaseValidator {
} }
} }
private boolean validateMeasureReportGroups(ValidatorHostContext hostContext, MeasureContext m, List<ValidationMessage> errors, Element mr, NodeStack stack, boolean inProgress) { private boolean validateMeasureReportGroups(ValidationContext hostContext, MeasureContext m, List<ValidationMessage> errors, Element mr, NodeStack stack, boolean inProgress) {
boolean ok = true; boolean ok = true;
if (m.groups().size() == 0) { if (m.groups().size() == 0) {
@ -344,7 +344,7 @@ public class MeasureValidator extends BaseValidator {
return "data-collection".equals(mr.getChildValue("type")); return "data-collection".equals(mr.getChildValue("type"));
} }
private boolean validateMeasureReportGroup(ValidatorHostContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack ns, boolean inProgress) { private boolean validateMeasureReportGroup(ValidationContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack ns, boolean inProgress) {
boolean ok = true; boolean ok = true;
ok = validateMeasureReportGroupPopulations(hostContext, m, mg, errors, mrg, ns, inProgress) && ok; ok = validateMeasureReportGroupPopulations(hostContext, m, mg, errors, mrg, ns, inProgress) && ok;
ok = validateScore(hostContext, m, errors, mrg, ns, inProgress) && ok; ok = validateScore(hostContext, m, errors, mrg, ns, inProgress) && ok;
@ -352,7 +352,7 @@ public class MeasureValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateScore(ValidatorHostContext hostContext, MeasureContext m, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) { private boolean validateScore(ValidationContext hostContext, MeasureContext m, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) {
boolean ok = true; boolean ok = true;
Element ms = mrg.getNamedChild("measureScore"); Element ms = mrg.getNamedChild("measureScore");
@ -456,7 +456,7 @@ public class MeasureValidator extends BaseValidator {
return true; return true;
} }
} }
private boolean validateMeasureReportGroupPopulations(ValidatorHostContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) { private boolean validateMeasureReportGroupPopulations(ValidationContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) {
boolean ok = true; boolean ok = true;
// there must be a population for each population defined in the measure, and no 4others. // there must be a population for each population defined in the measure, and no 4others.
List<MeasureGroupPopulationComponent> pops = new ArrayList<MeasureGroupPopulationComponent>(); List<MeasureGroupPopulationComponent> pops = new ArrayList<MeasureGroupPopulationComponent>();
@ -491,7 +491,7 @@ public class MeasureValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateMeasureReportGroupPopulation(ValidatorHostContext hostContext, MeasureContext m, MeasureGroupPopulationComponent mgp, List<ValidationMessage> errors, Element mrgp, NodeStack ns, boolean inProgress) { private boolean validateMeasureReportGroupPopulation(ValidationContext hostContext, MeasureContext m, MeasureGroupPopulationComponent mgp, List<ValidationMessage> errors, Element mrgp, NodeStack ns, boolean inProgress) {
boolean ok = true; boolean ok = true;
List<Element> sr = mrgp.getChildrenByName("subjectResults"); List<Element> sr = mrgp.getChildrenByName("subjectResults");
if ("subject-list".equals(m.reportType())) { if ("subject-list".equals(m.reportType())) {
@ -508,7 +508,7 @@ public class MeasureValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateMeasureReportGroupStratifiers(ValidatorHostContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) { private boolean validateMeasureReportGroupStratifiers(ValidationContext hostContext, MeasureContext m, MeasureGroupComponent mg, List<ValidationMessage> errors, Element mrg, NodeStack stack, boolean inProgress) {
boolean ok = true; boolean ok = true;
// there must be a population for each population defined in the measure, and no 4others. // there must be a population for each population defined in the measure, and no 4others.
@ -544,7 +544,7 @@ public class MeasureValidator extends BaseValidator {
return true; return true;
} }
private boolean validateMeasureReportGroupStratifier(ValidatorHostContext hostContext, MeasureContext m, MeasureGroupStratifierComponent mgs, List<ValidationMessage> errors, Element mrgs, NodeStack ns, boolean inProgress) { private boolean validateMeasureReportGroupStratifier(ValidationContext hostContext, MeasureContext m, MeasureGroupStratifierComponent mgs, List<ValidationMessage> errors, Element mrgs, NodeStack ns, boolean inProgress) {
// still to be done // still to be done
return true; return true;
} }

View File

@ -38,10 +38,10 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
import org.hl7.fhir.validation.instance.EnableWhenEvaluator; import org.hl7.fhir.validation.instance.utils.EnableWhenEvaluator;
import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack;
import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.hl7.fhir.validation.instance.utils.ValidationContext;
import org.hl7.fhir.validation.instance.utils.EnableWhenEvaluator.QStack;
import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ObjectUtil;
@ -431,7 +431,7 @@ public class QuestionnaireValidator extends BaseValidator {
return list; return list;
} }
public boolean validateQuestionannaireResponse(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException { public boolean validateQuestionannaireResponse(ValidationContext hostContext, List<ValidationMessage> errors, Element element, NodeStack stack) throws FHIRException {
if (questionnaireMode == QuestionnaireMode.NONE) { if (questionnaireMode == QuestionnaireMode.NONE) {
return true; return true;
} }
@ -481,7 +481,7 @@ public class QuestionnaireValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateQuestionnaireResponseItem(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { private boolean validateQuestionnaireResponseItem(ValidationContext hostContext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
BooleanHolder ok = new BooleanHolder(); BooleanHolder ok = new BooleanHolder();
String text = element.getNamedChildValue("text"); String text = element.getNamedChildValue("text");
@ -608,7 +608,7 @@ public class QuestionnaireValidator extends BaseValidator {
return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP;
} }
private boolean validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<ElementWithIndex> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { private boolean validateQuestionnaireResponseItem(ValidationContext hostcontext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<ElementWithIndex> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
boolean ok = true; boolean ok = true;
if (elements.size() > 1) { if (elements.size() > 1) {
ok = rulePlural(errors, NO_RULE_DATE, IssueType.INVALID, elements.get(1).getElement().line(), elements.get(1).getElement().col(), stack.getLiteralPath(), qItem.getRepeats(), elements.size(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEI, qItem.getLinkId()) && ok; ok = rulePlural(errors, NO_RULE_DATE, IssueType.INVALID, elements.get(1).getElement().line(), elements.get(1).getElement().col(), stack.getLiteralPath(), qItem.getRepeats(), elements.size(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEI, qItem.getLinkId()) && ok;
@ -628,7 +628,7 @@ public class QuestionnaireValidator extends BaseValidator {
return -1; return -1;
} }
private boolean validateQuestionannaireResponseItems(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { private boolean validateQuestionannaireResponseItems(ValidationContext hostContext, QuestionnaireWithContext qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
boolean ok = true; boolean ok = true;
List<Element> items = new ArrayList<Element>(); List<Element> items = new ArrayList<Element>();
element.getNamedChildren("item", items); element.getNamedChildren("item", items);
@ -673,7 +673,7 @@ public class QuestionnaireValidator extends BaseValidator {
return ok; return ok;
} }
public boolean validateQuestionnaireResponseItem(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List<ElementWithIndex> mapItem, QStack qstack) { public boolean validateQuestionnaireResponseItem(ValidationContext hostContext, QuestionnaireWithContext qsrc, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List<ElementWithIndex> mapItem, QStack qstack) {
boolean ok = true; boolean ok = true;
boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe); boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe);
if (mapItem != null) { if (mapItem != null) {

View File

@ -0,0 +1,27 @@
package org.hl7.fhir.validation.instance.utils;
import org.hl7.fhir.r5.model.CanonicalResource;
public class CanonicalResourceLookupResult {
CanonicalResource resource;
String error;
public CanonicalResourceLookupResult(CanonicalResource resource) {
this.resource = resource;
}
public CanonicalResourceLookupResult(String error) {
this.error = error;
}
public CanonicalResource getResource() {
return resource;
}
public String getError() {
return error;
}
}

View File

@ -0,0 +1,14 @@
package org.hl7.fhir.validation.instance.utils;
import java.util.Comparator;
import org.hl7.fhir.r5.model.CanonicalType;
public class CanonicalTypeSorter implements Comparator<CanonicalType> {
@Override
public int compare(CanonicalType o1, CanonicalType o2) {
return o1.getValue().compareTo(o2.getValue());
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.instance; package org.hl7.fhir.validation.instance.utils;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
@ -48,7 +48,6 @@ import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemOperator; import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemOperator;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator.QuestionnaireWithContext; import org.hl7.fhir.validation.instance.type.QuestionnaireValidator.QuestionnaireWithContext;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@ -144,7 +143,7 @@ public class EnableWhenEvaluator {
* <p> * <p>
* The context Questionnaire and QuestionnaireResponse are always available * The context Questionnaire and QuestionnaireResponse are always available
*/ */
public boolean isQuestionEnabled(ValidatorHostContext hostContext, QuestionnaireItemComponent qitem, QStack qstack, FHIRPathEngine engine) { public boolean isQuestionEnabled(ValidationContext hostContext, QuestionnaireItemComponent qitem, QStack qstack, FHIRPathEngine engine) {
if (hasExpressionExtension(qitem)) { if (hasExpressionExtension(qitem)) {
String expr = getExpression(qitem); String expr = getExpression(qitem);
ExpressionNode node = engine.parse(expr); ExpressionNode node = engine.parse(expr);

View File

@ -50,11 +50,11 @@ public class ResolvedReference {
return focus; return focus;
} }
public ValidatorHostContext hostContext(ValidatorHostContext hostContext, StructureDefinition profile) { public ValidationContext valContext(ValidationContext valContext, StructureDefinition profile) {
if (external) { if (external) {
return hostContext.forRemoteReference(profile, resource); return valContext.forRemoteReference(profile, resource);
} else { } else {
return hostContext.forLocalReference(profile, resource); return valContext.forLocalReference(profile, resource);
} }
} }
} }

View File

@ -0,0 +1,14 @@
package org.hl7.fhir.validation.instance.utils;
import java.util.Comparator;
import org.hl7.fhir.r5.model.StructureDefinition;
public class StructureDefinitionSorterByUrl implements Comparator<StructureDefinition> {
@Override
public int compare(StructureDefinition o1, StructureDefinition o2) {
return o1.getUrl().compareTo(o2.getUrl());
}
}

View File

@ -5,13 +5,18 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
public class ValidatorHostContext { public class ValidationContext {
private Object appContext; private Object appContext;
// the version we are currently validating for right now
// not implemented yet - this is the forerunner of a major upgrade to the validator
private String version;
// the resource we are actually validating right now // the resource we are actually validating right now
private Element resource; private Element resource;
// the resource that is the scope of id resolution - either the same as resource, or the resource the contains that resource. This can only be one level deep. // the resource that is the scope of id resolution - either the same as resource, or the resource the contains that resource. This can only be one level deep.
@ -19,14 +24,15 @@ public class ValidatorHostContext {
private Element groupingResource; // either a bundle or a parameters that holds the rootResource (for reference resolution) private Element groupingResource; // either a bundle or a parameters that holds the rootResource (for reference resolution)
private StructureDefinition profile; // the profile that contains the content being validated private StructureDefinition profile; // the profile that contains the content being validated
private boolean checkSpecials = true; private boolean checkSpecials = true;
private Map<String, List<ValidationMessage>> sliceRecords; private Map<String, List<ValidationMessage>> sliceRecords;
public ValidatorHostContext(Object appContext) { public ValidationContext(Object appContext) {
this.appContext = appContext; this.appContext = appContext;
} }
public ValidatorHostContext(Object appContext, Element element) { public ValidationContext(Object appContext, Element element) {
this.appContext = appContext; this.appContext = appContext;
this.resource = element; this.resource = element;
this.rootResource = element; this.rootResource = element;
@ -42,7 +48,7 @@ public class ValidatorHostContext {
} }
} }
public ValidatorHostContext(Object appContext, Element element, Element root) { public ValidationContext(Object appContext, Element element, Element root) {
this.appContext = appContext; this.appContext = appContext;
this.resource = element; this.resource = element;
this.rootResource = root; this.rootResource = root;
@ -51,7 +57,7 @@ public class ValidatorHostContext {
dump("creating"); dump("creating");
} }
public ValidatorHostContext(Object appContext, Element element, Element root, Element groupingResource) { public ValidationContext(Object appContext, Element element, Element root, Element groupingResource) {
this.appContext = appContext; this.appContext = appContext;
this.resource = element; this.resource = element;
this.rootResource = root; this.rootResource = root;
@ -64,12 +70,12 @@ public class ValidatorHostContext {
return appContext; return appContext;
} }
public ValidatorHostContext setAppContext(Object appContext) { public ValidationContext setAppContext(Object appContext) {
this.appContext = appContext; this.appContext = appContext;
return this; return this;
} }
public ValidatorHostContext setResource(Element resource) { public ValidationContext setResource(Element resource) {
this.resource = resource; this.resource = resource;
return this; return this;
} }
@ -78,7 +84,7 @@ public class ValidatorHostContext {
return rootResource; return rootResource;
} }
public ValidatorHostContext setRootResource(Element rootResource) { public ValidationContext setRootResource(Element rootResource) {
this.rootResource = rootResource; this.rootResource = rootResource;
dump("setting root resource"); dump("setting root resource");
return this; return this;
@ -92,7 +98,7 @@ public class ValidatorHostContext {
return profile; return profile;
} }
public ValidatorHostContext setProfile(StructureDefinition profile) { public ValidationContext setProfile(StructureDefinition profile) {
this.profile = profile; this.profile = profile;
return this; return this;
} }
@ -101,7 +107,7 @@ public class ValidatorHostContext {
return sliceRecords; return sliceRecords;
} }
public ValidatorHostContext setSliceRecords(Map<String, List<ValidationMessage>> sliceRecords) { public ValidationContext setSliceRecords(Map<String, List<ValidationMessage>> sliceRecords) {
this.sliceRecords = sliceRecords; this.sliceRecords = sliceRecords;
return this; return this;
} }
@ -124,45 +130,49 @@ public class ValidatorHostContext {
} }
} }
public ValidatorHostContext forContained(Element element) { public ValidationContext forContained(Element element) {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.rootResource = resource; res.rootResource = resource;
res.resource = element; res.resource = element;
res.profile = profile; res.profile = profile;
res.groupingResource = groupingResource; res.groupingResource = groupingResource;
res.version = version;
res.dump("forContained"); res.dump("forContained");
return res; return res;
} }
public ValidatorHostContext forEntry(Element element, Element groupingResource) { public ValidationContext forEntry(Element element, Element groupingResource) {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.rootResource = element; res.rootResource = element;
res.resource = element; res.resource = element;
res.profile = profile; res.profile = profile;
res.groupingResource = groupingResource; res.groupingResource = groupingResource;
res.version = version;
res.dump("forEntry"); res.dump("forEntry");
return res; return res;
} }
public ValidatorHostContext forProfile(StructureDefinition profile) { public ValidationContext forProfile(StructureDefinition profile) {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.resource = resource; res.resource = resource;
res.rootResource = rootResource; res.rootResource = rootResource;
res.profile = profile; res.profile = profile;
res.version = version;
res.groupingResource = groupingResource; res.groupingResource = groupingResource;
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>(); res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
res.dump("forProfile "+profile.getUrl()); res.dump("forProfile "+profile.getUrl());
return res; return res;
} }
public ValidatorHostContext forLocalReference(StructureDefinition profile, Element resource) { public ValidationContext forLocalReference(StructureDefinition profile, Element resource) {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.resource = resource; res.resource = resource;
res.rootResource = resource; res.rootResource = resource;
res.profile = profile; res.profile = profile;
res.groupingResource = groupingResource; res.groupingResource = groupingResource;
res.checkSpecials = false; res.checkSpecials = false;
res.dump("forLocalReference "+profile.getUrl()); res.dump("forLocalReference "+profile.getUrl());
res.version = version;
return res; return res;
} }
@ -173,28 +183,39 @@ public class ValidatorHostContext {
// } // }
} }
public ValidatorHostContext forRemoteReference(StructureDefinition profile, Element resource) { public ValidationContext forRemoteReference(StructureDefinition profile, Element resource) {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.resource = resource; res.resource = resource;
res.rootResource = resource; res.rootResource = resource;
res.profile = profile; res.profile = profile;
res.groupingResource = null; res.groupingResource = null;
res.checkSpecials = false; res.checkSpecials = false;
res.version = version;
res.dump("forRemoteReference "+profile.getUrl()); res.dump("forRemoteReference "+profile.getUrl());
return res; return res;
} }
public ValidatorHostContext forSlicing() { public ValidationContext forSlicing() {
ValidatorHostContext res = new ValidatorHostContext(appContext); ValidationContext res = new ValidationContext(appContext);
res.resource = resource; res.resource = resource;
res.rootResource = resource; res.rootResource = resource;
res.groupingResource = groupingResource; res.groupingResource = groupingResource;
res.profile = profile; res.profile = profile;
res.checkSpecials = false; res.checkSpecials = false;
res.version = version;
res.sliceRecords = new HashMap<String, List<ValidationMessage>>(); res.sliceRecords = new HashMap<String, List<ValidationMessage>>();
res.dump("forSlicing"); res.dump("forSlicing");
return res; return res;
} }
public String getVersion() {
return version;
}
public ValidationContext setVersion(String version) {
this.version = version;
return this;
}
} }