Add value set validation

This commit is contained in:
Grahame Grieve 2020-06-25 17:10:24 +10:00
parent 9dd0e06d9c
commit 44d7a59115
5 changed files with 125 additions and 4 deletions

View File

@ -506,4 +506,9 @@ public class I18nConstants {
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_TYPE_WRONG = "SEARCHPARAMETER_TYPE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
public static final String VALUESET_NO_SYSTEM_WARNING = "VALUESET_NO_SYSTEM_WARNING";
public static final String VALUESET_UNC_SYSTEM_WARNING = "VALUESET_UNC_SYSTEM_WARNING";
public static final String VALUESET_UNC_SYSTEM_WARNING_VER = "VALUESET_UNC_SYSTEM_WARNING_VER";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER";
}

View File

@ -507,3 +507,8 @@ SEARCHPARAMETER_NOTFOUND = Unable to find the base Search Parameter {0} so can't
SEARCHPARAMETER_BASE_WRONG = The base {1} is not listed as a base in the derivedFrom SearchParameter
SEARCHPARAMETER_TYPE_WRONG = The type {1} is different to the type {0} in the derivedFrom SearchParameter
SEARCHPARAMETER_TYPE_WRONG = The expression "{1}" is different to the expression "{0}" in the derivedFrom SearchParameter, and this likely indicates that the derivation relationship is not valid
VALUESET_NO_SYSTEM_WARNING = No System specified, so Concepts and Filters can't be checked
VALUESET_INCLUDE_INVALID_CONCEPT_CODE = The code {1} is not valid in the system {0}
VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = The code {2} is not valid in the system {0} version {1}
VALUESET_UNC_SYSTEM_WARNING = Unknown System specified, so Concepts and Filters can't be checked
VALUESET_UNC_SYSTEM_WARNING_VER = Unknown System/Version specified, so Concepts and Filters can't be checked

View File

@ -459,9 +459,10 @@ public class BaseValidator {
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.WARNING, null);
String message = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.WARNING, null);
}
return thePass;
}
@ -473,7 +474,7 @@ public class BaseValidator {
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
protected boolean warningHtml(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) {
addValidationMessage(errors, type, path, msg, html, IssueSeverity.WARNING, null);
}
@ -487,7 +488,7 @@ public class BaseValidator {
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
protected boolean warningHtml(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) {
String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, type, path, nmsg, html, IssueSeverity.WARNING, msg);

View File

@ -141,6 +141,7 @@ import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
import org.hl7.fhir.validation.instance.type.MeasureValidator;
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator;
import org.hl7.fhir.validation.instance.type.SearchParameterValidator;
import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
@ -3467,6 +3468,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
new CodeSystemValidator(context, timeTracker).validateCodeSystem(errors, element, stack);
} else if (element.getType().equals("SearchParameter")) {
new SearchParameterValidator(context, timeTracker).validateSearchParameter(errors, element, stack);
} else if (element.getType().equals("ValueSet")) {
new ValueSetValidator(context, timeTracker).validateValueSet(errors, element, stack);
}
}

View File

@ -0,0 +1,107 @@
package org.hl7.fhir.validation.instance.type;
import java.util.List;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
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;
public class ValueSetValidator extends BaseValidator {
public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) {
super(context);
source = Source.InstanceValidator;
this.timeTracker = timeTracker;
}
public void validateValueSet(List<ValidationMessage> errors, Element vs, NodeStack stack) {
if (!VersionUtilities.isR2Ver(context.getVersion())) {
List<Element> composes = vs.getChildrenByName("compose");
int cc = 0;
for (Element compose : composes) {
validateValueSetCompose(errors, compose, stack.push(compose, cc, null, null));
cc++;
}
}
}
private void validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack) {
List<Element> includes = compose.getChildrenByName("include");
int ci = 0;
for (Element include : includes) {
validateValueSetInclude(errors, include, stack.push(include, ci, null, null));
ci++;
}
List<Element> excludes = compose.getChildrenByName("exclude");
int ce = 0;
for (Element exclude : excludes) {
validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null));
ce++;
}
}
private void validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack) {
String system = include.getChildValue("system");
String version = include.getChildValue("version");
boolean systemOk = true;
List<Element> concepts = include.getChildrenByName("concept");
List<Element> filters = include.getChildrenByName("filter");
if (!Utilities.noString(system)) {
int cc = 0;
for (Element concept : concepts) {
if (systemOk && !validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)) {
systemOk = false;
}
cc++;
}
int cf = 0;
for (Element filter : filters) {
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
systemOk = false;
}
cf++;
}
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), systemOk, version == null ? I18nConstants.VALUESET_UNC_SYSTEM_WARNING : I18nConstants.VALUESET_UNC_SYSTEM_WARNING_VER);
} else {
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
}
}
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version) {
String code = concept.getChildValue("code");
if (version == null) {
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null);
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
return false;
} else {
boolean ok = vv.isOk();
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, code);
}
} else {
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null).setVersion(version), null);
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
return false;
} else {
boolean ok = vv.isOk();
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, code);
}
}
return true;
}
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
return true;
}
}