Refactoring ResourceValidationTracker
This commit is contained in:
parent
8c2679a200
commit
e54d7556b1
|
@ -36,11 +36,13 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.r5.model.Reference;
|
import org.hl7.fhir.r5.model.Reference;
|
||||||
import org.hl7.fhir.convertors.*;
|
import org.hl7.fhir.convertors.*;
|
||||||
import org.hl7.fhir.exceptions.DefinitionException;
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.exceptions.PathEngineException;
|
||||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||||
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
@ -114,6 +116,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
|
||||||
import org.hl7.fhir.r5.model.TimeType;
|
import org.hl7.fhir.r5.model.TimeType;
|
||||||
import org.hl7.fhir.r5.model.Timing;
|
import org.hl7.fhir.r5.model.Timing;
|
||||||
import org.hl7.fhir.r5.model.DataType;
|
import org.hl7.fhir.r5.model.DataType;
|
||||||
|
import org.hl7.fhir.r5.model.TypeDetails;
|
||||||
import org.hl7.fhir.r5.model.UriType;
|
import org.hl7.fhir.r5.model.UriType;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||||
|
@ -150,12 +153,11 @@ import ca.uhn.fhir.util.ObjectUtil;
|
||||||
/**
|
/**
|
||||||
* Thinking of using this in a java program? Don't!
|
* Thinking of using this in a java program? Don't!
|
||||||
* You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine
|
* You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine
|
||||||
*
|
* <p>
|
||||||
* Validation todo:
|
* Validation todo:
|
||||||
* - support @default slices
|
* - support @default slices
|
||||||
*
|
*
|
||||||
* @author Grahame Grieve
|
* @author Grahame Grieve
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* todo:
|
* todo:
|
||||||
|
@ -165,40 +167,159 @@ import ca.uhn.fhir.util.ObjectUtil;
|
||||||
|
|
||||||
public class InstanceValidator extends BaseValidator implements IResourceValidator {
|
public class InstanceValidator extends BaseValidator implements IResourceValidator {
|
||||||
|
|
||||||
/**
|
private class ValidatorHostServices implements IEvaluationContext {
|
||||||
* The validator keeps one of these classes for each resource it validates (e.g. when chasing references)
|
|
||||||
*
|
|
||||||
* It keeps track of the state of resource validation - a given resrouce may be validated agaisnt multiple
|
|
||||||
* profiles during a single validation, and it may be valid against some, and invalid against others.
|
|
||||||
*
|
|
||||||
* We don't want to keep doing the same validation, so we remember the outcomes for each combination
|
|
||||||
* of resource + profile
|
|
||||||
*
|
|
||||||
* Additionally, profile validation may be circular - e.g. a Patient profile that applies the same profile
|
|
||||||
* to linked patients, and 2 patient resources that link to each other. So this class also tracks validation
|
|
||||||
* in process.
|
|
||||||
*
|
|
||||||
* If the validator comes back to the same point, and validates the same instance against the same profile
|
|
||||||
* while it's already validating, it assumes it's valid - it cannot have new errors that wouldn't already
|
|
||||||
* be found in the first iteration - in other words, there's no errors
|
|
||||||
*
|
|
||||||
* @author graha
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private class ResourceValidationTracker {
|
|
||||||
private Map<String, List<ValidationMessage>> validations = new HashMap<>();
|
|
||||||
|
|
||||||
private void startValidating(StructureDefinition sd) {
|
@Override
|
||||||
validations.put(sd.getUrl(), new ArrayList<ValidationMessage>());
|
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
|
||||||
|
ValidatorHostContext c = (ValidatorHostContext) appContext;
|
||||||
|
if (externalHostServices != null)
|
||||||
|
return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ValidationMessage> getOutcomes(StructureDefinition sd) {
|
@Override
|
||||||
return validations.get(sd.getUrl());
|
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
|
||||||
|
ValidatorHostContext c = (ValidatorHostContext) appContext;
|
||||||
|
if (externalHostServices != null)
|
||||||
|
return externalHostServices.resolveConstantType(c.getAppContext(), name);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeOutcomes(StructureDefinition sd, List<ValidationMessage> errors) {
|
@Override
|
||||||
validations.put(sd.getUrl(), errors);
|
public boolean log(String argument, List<Base> focus) {
|
||||||
|
if (externalHostServices != null)
|
||||||
|
return externalHostServices.log(argument, focus);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionDetails resolveFunction(String functionName) {
|
||||||
|
throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
|
||||||
|
throw new Error("Not done yet (ValidatorHostServices.checkFunction)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
|
||||||
|
throw new Error("Not done yet (ValidatorHostServices.executeFunction)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException {
|
||||||
|
ValidatorHostContext c = (ValidatorHostContext) appContext;
|
||||||
|
|
||||||
|
if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) {
|
||||||
|
return (Base) refContext.getUserData("validator.bundle.resolution");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.getAppContext() instanceof Element) {
|
||||||
|
Element bnd = (Element) c.getAppContext();
|
||||||
|
Base res = resolveInBundle(url, bnd);
|
||||||
|
if (res != null)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Base res = resolveInBundle(url, c.getResource());
|
||||||
|
if (res != null)
|
||||||
|
return res;
|
||||||
|
res = resolveInBundle(url, c.getContainer());
|
||||||
|
if (res != null)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (externalHostServices != null)
|
||||||
|
return externalHostServices.resolveReference(c.getAppContext(), url, refContext);
|
||||||
|
else if (fetcher != null)
|
||||||
|
try {
|
||||||
|
return fetcher.fetch(c.getAppContext(), url);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new FHIRException(e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Error("Not done yet - resolve " + url + " locally (2)");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Base resolveInBundle(String url, Element bnd) {
|
||||||
|
if (bnd == null)
|
||||||
|
return null;
|
||||||
|
if (bnd.fhirType().equals("Bundle")) {
|
||||||
|
for (Element be : bnd.getChildrenByName("entry")) {
|
||||||
|
Element res = be.getNamedChild("resource");
|
||||||
|
if (res != null) {
|
||||||
|
String fullUrl = be.getChildValue("fullUrl");
|
||||||
|
String rt = res.fhirType();
|
||||||
|
String id = res.getChildValue("id");
|
||||||
|
if (url.equals(fullUrl))
|
||||||
|
return res;
|
||||||
|
if (url.equals(rt + "/" + id))
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
|
||||||
|
ValidatorHostContext ctxt = (ValidatorHostContext) appContext;
|
||||||
|
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
|
||||||
|
if (sd == null) {
|
||||||
|
throw new FHIRException("Unable to resolve " + url);
|
||||||
|
}
|
||||||
|
InstanceValidator self = InstanceValidator.this;
|
||||||
|
List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>();
|
||||||
|
if (item instanceof Resource) {
|
||||||
|
try {
|
||||||
|
Element e = new ObjectConverter(context).convert((Resource) item);
|
||||||
|
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e));
|
||||||
|
} catch (IOException e1) {
|
||||||
|
throw new FHIRException(e1);
|
||||||
|
}
|
||||||
|
} else if (item instanceof Element) {
|
||||||
|
Element e = (Element) item;
|
||||||
|
if (e.isResource()) {
|
||||||
|
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e));
|
||||||
|
} else {
|
||||||
|
throw new FHIRException("Not supported yet");
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element");
|
||||||
|
boolean ok = true;
|
||||||
|
List<ValidationMessage> record = new ArrayList<>();
|
||||||
|
for (ValidationMessage v : valerrors) {
|
||||||
|
ok = ok && !v.getLevel().isError();
|
||||||
|
if (v.getLevel().isError() || v.isSlicingHint()) {
|
||||||
|
record.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok && !record.isEmpty()) {
|
||||||
|
ctxt.sliceNotes(url, record);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSet resolveValueSet(Object appContext, String url) {
|
||||||
|
ValidatorHostContext c = (ValidatorHostContext) appContext;
|
||||||
|
if (c.getProfile() != null && url.startsWith("#")) {
|
||||||
|
for (Resource r : c.getProfile().getContained()) {
|
||||||
|
if (r.getId().equals(url.substring(1))) {
|
||||||
|
if (r instanceof ValueSet)
|
||||||
|
return (ValueSet) r;
|
||||||
|
else
|
||||||
|
throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return context.fetchResource(ValueSet.class, url);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkerContext context;
|
private IWorkerContext context;
|
||||||
|
@ -254,7 +375,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
this.context = theContext;
|
this.context = theContext;
|
||||||
this.externalHostServices = hostServices;
|
this.externalHostServices = hostServices;
|
||||||
fpe = new FHIRPathEngine(context);
|
fpe = new FHIRPathEngine(context);
|
||||||
validatorServices = new ValidatorHostServices(this);
|
validatorServices = new ValidatorHostServices();
|
||||||
fpe.setHostServices(validatorServices);
|
fpe.setHostServices(validatorServices);
|
||||||
if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0"))
|
if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0"))
|
||||||
fpe.setLegacyMode(true);
|
fpe.setLegacyMode(true);
|
||||||
|
@ -673,8 +794,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated");
|
hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated");
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1401,7 +1521,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
|
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
|
||||||
if (ok) { break; }
|
if (ok) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (ctxt.getType() == ExtensionContextType.ELEMENT) {
|
if (ctxt.getType() == ExtensionContextType.ELEMENT) {
|
||||||
String en = ctxt.getExpression();
|
String en = ctxt.getExpression();
|
||||||
contexts.append("e:" + en);
|
contexts.append("e:" + en);
|
||||||
|
@ -1411,7 +1533,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
for (String p : plist) {
|
for (String p : plist) {
|
||||||
if (ok) {break; }
|
if (ok) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (p.equals(en)) {
|
if (p.equals(en)) {
|
||||||
ok = true;
|
ok = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2946,15 +3070,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @param element - the candidate that might be in the slice
|
||||||
* @param element
|
* @param path - for reporting any errors. the XPath for the element
|
||||||
* - the candidate that might be in the slice
|
* @param slicer - the definition of how slicing is determined
|
||||||
* @param path
|
* @param ed - the slice for which to test membership
|
||||||
* - for reporting any errors. the XPath for the element
|
|
||||||
* @param slicer
|
|
||||||
* - the definition of how slicing is determined
|
|
||||||
* @param ed
|
|
||||||
* - the slice for which to test membership
|
|
||||||
* @param errors
|
* @param errors
|
||||||
* @param stack
|
* @param stack
|
||||||
* @return
|
* @return
|
||||||
|
@ -3145,7 +3264,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
for (Coding c : cc.getCoding()) {
|
for (Coding c : cc.getCoding()) {
|
||||||
if (c.hasExtension())
|
if (c.hasExtension())
|
||||||
throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId());
|
throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId());
|
||||||
if (firstCoding) firstCoding = false; else expression.append(" and ");
|
if (firstCoding) firstCoding = false;
|
||||||
|
else expression.append(" and ");
|
||||||
expression.append(discriminator + ".coding.where(");
|
expression.append(discriminator + ".coding.where(");
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
if (c.hasSystem()) {
|
if (c.hasSystem()) {
|
||||||
|
@ -3153,15 +3273,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
expression.append("system = '" + c.getSystem() + "'");
|
expression.append("system = '" + c.getSystem() + "'");
|
||||||
}
|
}
|
||||||
if (c.hasVersion()) {
|
if (c.hasVersion()) {
|
||||||
if (first) first = false; else expression.append(" and ");
|
if (first) first = false;
|
||||||
|
else expression.append(" and ");
|
||||||
expression.append("version = '" + c.getVersion() + "'");
|
expression.append("version = '" + c.getVersion() + "'");
|
||||||
}
|
}
|
||||||
if (c.hasCode()) {
|
if (c.hasCode()) {
|
||||||
if (first) first = false; else expression.append(" and ");
|
if (first) first = false;
|
||||||
|
else expression.append(" and ");
|
||||||
expression.append("code = '" + c.getCode() + "'");
|
expression.append("code = '" + c.getCode() + "'");
|
||||||
}
|
}
|
||||||
if (c.hasDisplay()) {
|
if (c.hasDisplay()) {
|
||||||
if (first) first = false; else expression.append(" and ");
|
if (first) first = false;
|
||||||
|
else expression.append(" and ");
|
||||||
expression.append("display = '" + c.getDisplay() + "'");
|
expression.append("display = '" + c.getDisplay() + "'");
|
||||||
}
|
}
|
||||||
expression.append(").exists()");
|
expression.append(").exists()");
|
||||||
|
@ -3501,6 +3624,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
iRest++;
|
iRest++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
private void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
||||||
String url = cs.getNamedChildValue("url");
|
String url = cs.getNamedChildValue("url");
|
||||||
String vsu = cs.getNamedChildValue("valueSet");
|
String vsu = cs.getNamedChildValue("valueSet");
|
||||||
|
@ -3558,7 +3682,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id);
|
new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id);
|
||||||
byte[] json = bs.toByteArray();
|
byte[] json = bs.toByteArray();
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case DSTU1: throw new FHIRException("Unsupported version R1");
|
case DSTU1:
|
||||||
|
throw new FHIRException("Unsupported version R1");
|
||||||
case DSTU2:
|
case DSTU2:
|
||||||
org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json);
|
org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json);
|
||||||
Resource r5 = VersionConvertor_10_50.convertResource(r2);
|
Resource r5 = VersionConvertor_10_50.convertResource(r2);
|
||||||
|
@ -3674,7 +3799,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false);
|
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false);
|
||||||
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
|
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
|
||||||
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
|
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
|
||||||
else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer");
|
else if (itemType.equals("integer"))
|
||||||
|
checkOption(errors, answer, ns, qsrc, qItem, "integer");
|
||||||
else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string");
|
else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -3684,8 +3810,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true);
|
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true);
|
||||||
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
|
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
|
||||||
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
|
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
|
||||||
else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer");
|
else if (itemType.equals("integer"))
|
||||||
else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string", true);
|
checkOption(errors, answer, ns, qsrc, qItem, "integer");
|
||||||
|
else if (itemType.equals("string"))
|
||||||
|
checkOption(errors, answer, ns, qsrc, qItem, "string", true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// case QUESTION:
|
// case QUESTION:
|
||||||
|
@ -3746,12 +3874,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
|
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
|
||||||
NodeStack ns = stack.push(item, -1, null, null);
|
NodeStack ns = stack.push(item, -1, null, null);
|
||||||
validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item));
|
validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item));
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire");
|
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order");
|
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order");
|
||||||
lastIndex = index;
|
lastIndex = index;
|
||||||
|
|
||||||
|
@ -4168,6 +4293,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
* Check each resource entry to ensure that the entry's fullURL includes the resource's id
|
* Check each resource entry to ensure that the entry's fullURL includes the resource's id
|
||||||
* value. Adds an ERROR ValidationMessge to errors List for a given entry if it references
|
* value. Adds an ERROR ValidationMessge to errors List for a given entry if it references
|
||||||
* a resource and fullURL does not include the resource's id.
|
* a resource and fullURL does not include the resource's id.
|
||||||
|
*
|
||||||
* @param errors List of ValidationMessage objects that new errors will be added to.
|
* @param errors List of ValidationMessage objects that new errors will be added to.
|
||||||
* @param entries List of entry Element objects to be checked.
|
* @param entries List of entry Element objects to be checked.
|
||||||
* @param stack Current NodeStack used to create path names in error detail messages.
|
* @param stack Current NodeStack used to create path names in error detail messages.
|
||||||
|
@ -5006,7 +5132,8 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
}
|
}
|
||||||
if (resp != null) {
|
if (resp != null) {
|
||||||
return IdStatus.OPTIONAL;
|
return IdStatus.OPTIONAL;
|
||||||
} if (method == null) {
|
}
|
||||||
|
if (method == null) {
|
||||||
if (fullUrl == null)
|
if (fullUrl == null)
|
||||||
return IdStatus.REQUIRED;
|
return IdStatus.REQUIRED;
|
||||||
else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:"))
|
else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:"))
|
||||||
|
@ -5121,7 +5248,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
/*
|
/*
|
||||||
* The actual base entry point for internal use (re-entrant)
|
* The actual base entry point for internal use (re-entrant)
|
||||||
*/
|
*/
|
||||||
public void validateResource(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException {
|
private void validateResource(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException {
|
||||||
assert stack != null;
|
assert stack != null;
|
||||||
assert resource != null;
|
assert resource != null;
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
|
@ -5224,7 +5351,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
return Calendar.getInstance().get(Calendar.YEAR);
|
return Calendar.getInstance().get(Calendar.YEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NodeStack {
|
public class NodeStack {
|
||||||
private ElementDefinition definition;
|
private ElementDefinition definition;
|
||||||
private Element element;
|
private Element element;
|
||||||
private ElementDefinition extension;
|
private ElementDefinition extension;
|
||||||
|
@ -5293,9 +5420,11 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) {
|
private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) {
|
||||||
return pushInternal(element, count, definition, type, "->");
|
return pushInternal(element, count, definition, type, "->");
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) {
|
private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) {
|
||||||
return pushInternal(element, count, definition, type, ".");
|
return pushInternal(element, count, definition, type, ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) {
|
private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) {
|
||||||
NodeStack res = new NodeStack();
|
NodeStack res = new NodeStack();
|
||||||
res.parent = this;
|
res.parent = this;
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.hl7.fhir.r5.validation.instancevalidator.utils;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The validator keeps one of these classes for each resource it validates (e.g. when chasing references)
|
||||||
|
* <p>
|
||||||
|
* It keeps track of the state of resource validation - a given resrouce may be validated agaisnt multiple
|
||||||
|
* profiles during a single validation, and it may be valid against some, and invalid against others.
|
||||||
|
* <p>
|
||||||
|
* We don't want to keep doing the same validation, so we remember the outcomes for each combination
|
||||||
|
* of resource + profile
|
||||||
|
* <p>
|
||||||
|
* Additionally, profile validation may be circular - e.g. a Patient profile that applies the same profile
|
||||||
|
* to linked patients, and 2 patient resources that link to each other. So this class also tracks validation
|
||||||
|
* in process.
|
||||||
|
* <p>
|
||||||
|
* If the validator comes back to the same point, and validates the same instance against the same profile
|
||||||
|
* while it's already validating, it assumes it's valid - it cannot have new errors that wouldn't already
|
||||||
|
* be found in the first iteration - in other words, there's no errors
|
||||||
|
*
|
||||||
|
* @author graha
|
||||||
|
*/
|
||||||
|
public class ResourceValidationTracker {
|
||||||
|
private Map<String, List<ValidationMessage>> validations = new HashMap<>();
|
||||||
|
|
||||||
|
public void startValidating(StructureDefinition sd) {
|
||||||
|
validations.put(sd.getUrl(), new ArrayList<ValidationMessage>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ValidationMessage> getOutcomes(StructureDefinition sd) {
|
||||||
|
return validations.get(sd.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void storeOutcomes(StructureDefinition sd, List<ValidationMessage> errors) {
|
||||||
|
validations.put(sd.getUrl(), errors);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue