Fix bug validating mime types without terminology serverBase (warning, not error)

Performance improvements in JSON metadata based parser
Add first round of supplement validation
improve error message on profile validation fail
fix NPE validating some slices
fix bug validating canonicals as part of choice data types
Adds special support for http://hl7.org/fhirpath/System.* types
fix bug matching slices in contained resources that have references to #
This commit is contained in:
Grahame Grieve 2021-02-08 09:40:25 +11:00
parent 2413ec1dbb
commit ef3b8c1f0a
13 changed files with 137 additions and 47 deletions

View File

@ -750,7 +750,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
// 3rd pass: hit the server
for (CodingValidationRequest t : codes) {
t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs) : null);
codeSystemsUsed.add(t.getCoding().getSystem());
if (t.getCoding().hasSystem()) {
codeSystemsUsed.add(t.getCoding().getSystem());
}
if (txCache != null) {
t.setResult(txCache.getValidation(t.getCacheToken()));
}
@ -846,7 +848,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
options = ValidationOptions.defaults();
}
codeSystemsUsed.add(code.getSystem());
if (code.hasSystem()) {
codeSystemsUsed.add(code.getSystem());
}
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null;
if (txCache != null) {
@ -922,7 +926,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return res;
}
for (Coding c : code.getCoding()) {
codeSystemsUsed.add(c.getSystem());
if (c.hasSystem()) {
codeSystemsUsed.add(c.getSystem());
}
}
if (options.isUseClient()) {
@ -1134,6 +1140,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return noTerminologyServer;
}
public void setNoTerminologyServer(boolean noTerminologyServer) {
this.noTerminologyServer = noTerminologyServer;
}
public String getName() {
return name;
}

View File

@ -76,15 +76,19 @@ public class JsonParser extends ParserBase {
private Map<JsonElement, LocationData> map;
private boolean allowComments;
private FHIRPathEngine fpe;
private ProfileUtilities profileUtilities;
public JsonParser(IWorkerContext context) {
super(context);
public JsonParser(IWorkerContext context, ProfileUtilities utilities) {
super(context);
this.fpe = new FHIRPathEngine(this.context);
this.profileUtilities = new ProfileUtilities(this.context, null, null, this.fpe);
}
this.profileUtilities = utilities;
}
public JsonParser(IWorkerContext context) {
super(context);
this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context));
}
public Element parse(String source, String type) throws Exception {
JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source);

View File

@ -152,6 +152,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
throw new FHIRException(warningMessage);
}
}
if (cs != null && cs.hasSupplements()) {
return new ValidationResult(IssueSeverity.ERROR, context.formatMessage(I18nConstants.CODESYSTEM_CS_NO_SUPPLEMENT, cs.getUrl()));
}
if (cs!=null && cs.getContent() != CodeSystemContentMode.COMPLETE) {
warningMessage = "Resolved system "+system+", but the definition is not complete";
if (!inExpansion && cs.getContent() != CodeSystemContentMode.FRAGMENT) { // we're going to give it a go if it's a fragment

View File

@ -304,7 +304,6 @@ public class FHIRPathEngine {
public ValueSet resolveValueSet(Object appContext, String url);
}
/**
* @param worker - used when validating paths (@check), and used doing value set membership when executing tests (once that's defined)
*/

View File

@ -50,6 +50,9 @@ public class I18nConstants {
public static final String CODESYSTEM_CS_VS_INVALID = "CodeSystem_CS_VS_Invalid";
public static final String CODESYSTEM_CS_VS_EXP_MISMATCH = "CODESYSTEM_CS_VS_EXP_MISMATCH";
public static final String CODESYSTEM_CS_VS_WRONGSYSTEM = "CodeSystem_CS_VS_WrongSystem";
public static final String CODESYSTEM_CS_NO_SUPPLEMENT = "CODESYSTEM_CS_NO_SUPPLEMENT";
public static final String CODESYSTEM_CS_SUPP_CANT_CHECK = "CODESYSTEM_CS_SUPP_CANT_CHECK";
public static final String CODESYSTEM_CS_SUPP_INVALID_CODE = "CODESYSTEM_CS_SUPP_INVALID_CODE";
public static final String CODE_FOUND_IN_EXPANSION_HOWEVER_ = "Code_found_in_expansion_however_";
public static final String CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE = "Coding_has_no_system__cannot_validate";
public static final String CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_ = "Contained_resource_does_not_appear_to_be_a_FHIR_resource_unknown_name_";

View File

@ -3,19 +3,19 @@ package org.hl7.fhir.utilities.validation;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@ -26,7 +26,7 @@ package org.hl7.fhir.utilities.validation;
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
@ -127,6 +127,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case ERROR: return "error";
case WARNING: return "warning";
case INFORMATION: return "information";
case NULL: return null;
default: return "?";
}
}
@ -136,6 +137,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case ERROR: return "http://hl7.org/fhir/issue-severity";
case WARNING: return "http://hl7.org/fhir/issue-severity";
case INFORMATION: return "http://hl7.org/fhir/issue-severity";
case NULL: return null;
default: return "?";
}
}
@ -145,6 +147,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case ERROR: return "The issue is sufficiently important to cause the action to fail.";
case WARNING: return "The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.";
case INFORMATION: return "The issue has no relation to the degree of success of the action.";
case NULL: return null;
default: return "?";
}
}
@ -154,6 +157,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case ERROR: return "Error";
case WARNING: return "Warning";
case INFORMATION: return "Information";
case NULL: return null;
default: return "?";
}
}
@ -380,6 +384,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case TIMEOUT: return "timeout";
case THROTTLED: return "throttled";
case INFORMATIONAL: return "informational";
case NULL: return null;
default: return "?";
}
}
@ -414,6 +419,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case TIMEOUT: return "http://hl7.org/fhir/issue-type";
case THROTTLED: return "http://hl7.org/fhir/issue-type";
case INFORMATIONAL: return "http://hl7.org/fhir/issue-type";
case NULL: return null;
default: return "?";
}
}
@ -448,6 +454,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case TIMEOUT: return "An internal timeout has occurred.";
case THROTTLED: return "The system is not prepared to handle this request due to load management.";
case INFORMATIONAL: return "A message unrelated to the processing success of the completed operation (examples of the latter include things like reminders of password expiry, system maintenance times, etc.).";
case NULL: return null;
default: return "?";
}
}
@ -482,6 +489,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
case TIMEOUT: return "Timeout";
case THROTTLED: return "Throttled";
case INFORMATIONAL: return "Informational Note";
case NULL: return null;
default: return "?";
}
}
@ -692,7 +700,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
public String getDisplay() {
return level + ": " + (location==null || location.isEmpty() ? "" : (location + ": ")) + message;
}
/**
* Returns a representation of this ValidationMessage suitable for logging. The values of
* most of the internal fields are included, so this may not be suitable for display to
@ -781,5 +789,5 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
this.signpost = signpost;
}
}

View File

@ -106,8 +106,8 @@ Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can''t ref
Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference ({1})
Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1})
Reference_REF_BadTargetType2 = The type ''{0}'' implied by the reference URL {1} is not a valid Target for this element (must be one of {2})
Reference_REF_CantMatchChoice = Unable to find matching profile for {0} among choices: {1}
Reference_REF_CantMatchType = Unable to find matching profile for {0} (by type) among choices: {1}
Reference_REF_CantMatchChoice = Unable to find a match for profile {0} among choices: {1}
Reference_REF_CantMatchType = Unable to find a match for profile {0} (by type) among choices: {1}
Reference_REF_CantResolve = Unable to resolve resource ''{0}''
Reference_REF_CantResolveProfile = Unable to resolve the profile reference ''{0}''
Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search URL is allowed ([type]?parameters. Encountered {0})
@ -217,7 +217,7 @@ Validation_VAL_Profile_NoCheckMax = {2}: Unable to check max allowed ({1}) due t
Validation_VAL_Profile_NoCheckMin = {2}: Unable to check minimum required ({1}) due to lack of slicing validation (from {0})
Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0}
Validation_VAL_Profile_NoDefinition = No definition found for resource type ''{0}''
Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0}
Validation_VAL_Profile_NoMatch = Unable to find a match for the specified profile among choices: {0}
Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided
Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1}
Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0}
@ -636,3 +636,6 @@ DISCRIMINATOR_BAD_PATH = Error processing path expression for disciminator: {0}
SLICING_CANNOT_BE_EVALUATED = Slicing cannot be evaluated: {0}
TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = Canonical URL ''{0}'' does not resolve
TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = Canonical URL ''{0}'' refers to a resource that has the wrong type. Found {1} expecting one of {2}
CODESYSTEM_CS_NO_SUPPLEMENT = CodeSystem {0} is a supplement, so can't be used as a value in Coding.system
CODESYSTEM_CS_SUPP_CANT_CHECK = CodeSystem {0} cannot be found, so can't check if concepts are valid
CODESYSTEM_CS_SUPP_INVALID_CODE = The code ''{1}'' is not declared in the base CodeSystem {0} so is not valid in the supplement

View File

@ -834,6 +834,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
context.setTlogging(false);
if (url == null) {
context.setCanRunWithoutTerminology(true);
context.setNoTerminologyServer(true);
return "n/a: No Terminology Server";
} else {
try {

View File

@ -129,10 +129,10 @@ public class Params {
cliContext.getBundleValidationRules().add(new BundleValidationRule(r, p));
} else if (args[i].equals(QUESTIONNAIRE)) {
if (i + 1 == args.length)
throw new Error("Specified -questionnaire without indicating questionnaire file");
throw new Error("Specified -questionnaire without indicating questionnaire mode");
else {
String q = args[++i];
cliContext.setQuestionnaireMode(QuestionnaireMode.valueOf(q));
cliContext.setQuestionnaireMode(QuestionnaireMode.fromCode(q));
}
} else if (args[i].equals(NATIVE)) {
cliContext.setDoNative(true);

View File

@ -27,6 +27,7 @@ public class ProfileLoader {
try {
URL url = new URL(src + "?nocache=" + System.currentTimeMillis());
URLConnection c = url.openConnection();
return IOUtils.toByteArray(c.getInputStream());
} catch (Exception e) {
throw new FHIRException("Unable to find definitions at URL '" + src + "': " + e.getMessage(), e);

View File

@ -660,7 +660,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override
public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List<ValidationMessage> errors, JsonObject object, List<StructureDefinition> profiles) throws FHIRException {
JsonParser parser = new JsonParser(context);
JsonParser parser = new JsonParser(context, new ProfileUtilities(context, null, null, fpe));
parser.setupValidation(ValidationPolicy.EVERYTHING, errors);
long t = System.nanoTime();
Element e = parser.parse(object);
@ -2158,6 +2158,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return true;
}
}
if (tr.getTargetProfile().isEmpty()) {
return true;
}
}
return false;
}
@ -2400,7 +2403,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value, stack);
else if (!noExtensibleWarnings)
else if (!noExtensibleWarnings && !isOkExtension(value, vs))
txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage()));
} else if (binding.getStrength() == BindingStrength.PREFERRED) {
if (baseOnly) {
@ -2413,6 +2416,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE2);
}
private boolean isOkExtension(String value, ValueSet vs) {
if ("http://hl7.org/fhir/ValueSet/defined-types".equals(vs.getUrl())) {
return value.startsWith("http://hl7.org/fhirpath/System.");
}
return false;
}
private void checkQuantity(List<ValidationMessage> errors, String path, Element focus, Quantity fixed, String fixedSource, boolean pattern) {
checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern);
checkFixedValue(errors, path + ".comparator", focus.getNamedChild("comparator"), fixed.getComparatorElement(), fixedSource, "comparator", focus, pattern);
@ -3166,36 +3176,53 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// really, there should only be one level for this (contained resources cannot contain
// contained resources), but we'll leave that to some other code to worry about
boolean wasContained = false;
while (stack != null && stack.getElement() != null) {
if (stack.getElement().getProperty().isResource()) {
NodeStack nstack = stack;
while (nstack != null && nstack.getElement() != null) {
if (nstack.getElement().getProperty().isResource()) {
// ok, we'll try to find the contained reference
if (ref.equals("#") && stack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) {
if (ref.equals("#") && nstack.getElement().getSpecial() != SpecialElement.CONTAINED && wasContained) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(stack.getElement());
rr.setFocus(stack.getElement());
rr.setResource(nstack.getElement());
rr.setFocus(nstack.getElement());
rr.setExternal(false);
rr.setStack(stack.push(stack.getElement(), -1, stack.getElement().getProperty().getDefinition(), stack.getElement().getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+stack.getElement().fhirType()+")");
rr.setStack(nstack.push(nstack.getElement(), -1, nstack.getElement().getProperty().getDefinition(), nstack.getElement().getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")");
return rr;
}
if (stack.getElement().getSpecial() == SpecialElement.CONTAINED) {
if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) {
wasContained = true;
}
IndexedElement res = getContainedById(stack.getElement(), ref.substring(1));
IndexedElement res = getContainedById(nstack.getElement(), ref.substring(1));
if (res != null) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(stack.getElement());
rr.setResource(nstack.getElement());
rr.setFocus(res.getMatch());
rr.setExternal(false);
rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+stack.getElement().fhirType()+")");
rr.setStack(nstack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")");
return rr;
}
}
if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || stack.getElement().getSpecial() == SpecialElement.PARAMETER) {
if (nstack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || nstack.getElement().getSpecial() == SpecialElement.PARAMETER) {
return null; // we don't try to resolve contained references across this boundary
}
stack = stack.getParent();
nstack = nstack.getParent();
}
// try again, and work up the element parent list
if (ref.equals("#")) {
Element e = stack.getElement();
while (e != null) {
if (e.getProperty().isResource() && (e.getSpecial() != SpecialElement.CONTAINED)) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(e);
rr.setFocus(e);
rr.setExternal(false);
rr.setStack(stack.push(e, -1, e.getProperty().getDefinition(), e.getProperty().getDefinition()));
rr.getStack().qualifyPath(".ofType("+e.fhirType()+")");
return rr;
}
e = e.getParentForValidator();
}
}
return null;
} else {
@ -4007,7 +4034,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (element.getType().equals("CapabilityStatement")) {
validateCapabilityStatement(errors, element, stack);
} else if (element.getType().equals("CodeSystem")) {
new CodeSystemValidator(context, timeTracker, xverManager).validateCodeSystem(errors, element, stack);
new CodeSystemValidator(context, timeTracker, xverManager).validateCodeSystem(errors, element, stack, new ValidationOptions(stack.getWorkingLang()));
} else if (element.getType().equals("SearchParameter")) {
new SearchParameterValidator(context, timeTracker, fpe, xverManager).validateSearchParameter(errors, element, stack);
} else if (element.getType().equals("StructureDefinition")) {
@ -4951,15 +4978,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (inv.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice") &&
ToolingExtensions.readBooleanExtension(inv, "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice")) {
if (bpWarnings == BestPracticeWarningLevel.Hint)
hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]");
hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg));
else if (bpWarnings == BestPracticeWarningLevel.Warning)
warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]");
warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg));
else if (bpWarnings == BestPracticeWarningLevel.Error)
rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]");
rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg));
} else if (inv.getSeverity() == ConstraintSeverity.ERROR) {
rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]");
rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg));
} else if (inv.getSeverity() == ConstraintSeverity.WARNING) {
warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]");
warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + (Utilities.noString(msg) ? "failed" : msg));
}
}
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.Utilities;
@ -12,10 +13,13 @@ import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.TimeTracker;
import org.hl7.fhir.validation.instance.utils.NodeStack;
import ca.uhn.fhir.validation.ValidationResult;
public class CodeSystemValidator extends BaseValidator {
public CodeSystemValidator(IWorkerContext context, TimeTracker timeTracker, XVerExtensionManager xverManager) {
@ -24,7 +28,7 @@ public class CodeSystemValidator extends BaseValidator {
this.timeTracker = timeTracker;
}
public void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
public void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack, ValidationOptions options) {
String url = cs.getNamedChildValue("url");
String content = cs.getNamedChildValue("content");
@ -52,6 +56,31 @@ public class CodeSystemValidator extends BaseValidator {
}
}
} // todo... try getting the value set the other way...
String supp = cs.getNamedChildValue("supplements");
if (supp != null) {
if (context.supportsSystem(supp)) {
List<Element> concepts = cs.getChildrenByName("concept");
int ce = 0;
for (Element concept : concepts) {
validateSupplementConcept(errors, concept, stack.push(concept, ce, null, null), supp, options);
ce++;
}
} else {
if (cs.hasChildren("concept")) {
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_SUPP_CANT_CHECK, supp);
}
}
}
}
private void validateSupplementConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String supp, ValidationOptions options) {
String code = concept.getChildValue("code");
if (!Utilities.noString(code)) {
org.hl7.fhir.r5.context.IWorkerContext.ValidationResult res = context.validateCode(options, supp, code, null);
rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), res.isOk(), I18nConstants.CODESYSTEM_CS_SUPP_INVALID_CODE, supp, code);
}
}
private int countConcepts(Element cs) {

View File

@ -86,7 +86,9 @@ public class ValidatorHostContext {
}
public void sliceNotes(String url, List<ValidationMessage> record) {
if (sliceRecords != null) {
sliceRecords.put(url, record);
}
}
public ValidatorHostContext forContained(Element element) {