missed code

This commit is contained in:
Grahame Grieve 2024-03-07 13:48:21 +11:00
parent 19bd64f81a
commit 1bab5bcb5b

View File

@ -1,11 +1,15 @@
package org.hl7.fhir.validation.instance.type; package org.hl7.fhir.validation.instance.type;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -13,6 +17,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
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.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidationContext;
public class CodeSystemValidator extends BaseValidator { public class CodeSystemValidator extends BaseValidator {
@ -20,7 +25,7 @@ public class CodeSystemValidator extends BaseValidator {
super(parent); super(parent);
} }
public boolean validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack, ValidationOptions options) { public boolean validateCodeSystem(ValidationContext valContext, List<ValidationMessage> errors, Element cs, NodeStack stack, ValidationOptions options) {
boolean ok = true; boolean ok = true;
String url = cs.getNamedChildValue("url", false); String url = cs.getNamedChildValue("url", false);
String content = cs.getNamedChildValue("content", false); String content = cs.getNamedChildValue("content", false);
@ -28,13 +33,14 @@ public class CodeSystemValidator extends BaseValidator {
String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning", false); String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning", false);
String supp = cs.getNamedChildValue("supplements", false); String supp = cs.getNamedChildValue("supplements", false);
int count = countConcepts(cs); int count = countConcepts(cs);
CodeSystem csB = null;
metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp), count, supp); metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp), count, supp);
String vsu = cs.getNamedChildValue("valueSet", false); String vsu = cs.getNamedChildValue("valueSet", false);
if (!Utilities.noString(vsu)) { if (!Utilities.noString(vsu)) {
if ("supplement".equals(content)) { if ("supplement".equals(content)) {
CodeSystem csB = context.fetchCodeSystem(supp); csB = context.fetchCodeSystem(supp);
if (csB != null) { if (csB != null) {
if (csB.hasValueSet()) { if (csB.hasValueSet()) {
warning(errors, "2024-03-06", IssueType.BUSINESSRULE, stack.getLiteralPath(), vsu.equals(vsu), I18nConstants.CODESYSTEM_CS_NO_VS_SUPPLEMENT2, csB.getValueSet()); warning(errors, "2024-03-06", IssueType.BUSINESSRULE, stack.getLiteralPath(), vsu.equals(vsu), I18nConstants.CODESYSTEM_CS_NO_VS_SUPPLEMENT2, csB.getValueSet());
@ -99,11 +105,58 @@ public class CodeSystemValidator extends BaseValidator {
if (!stack.isContained()) { if (!stack.isContained()) {
ok = checkShareableCodeSystem(errors, cs, stack) && ok; ok = checkShareableCodeSystem(errors, cs, stack) && ok;
} else {
// we approve of contained code systems in two circumstances:
// * inside a questionnaire for a code system only used by that questionnaire
// * inside a supplement, for creating properties in the supplement// otherwise, we put a hint on it that this is probably a bad idea
boolean isInQ = valContext.getRootResource() != null && valContext.getRootResource().fhirType().equals("Questionnaire");
boolean isSuppProp = valContext.getRootResource() != null && valContext.getRootResource().fhirType().equals("CodeSystem"); // todo add more checks
hint(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), !isInQ && !isSuppProp, I18nConstants.CODESYSTEM_NOT_CONTAINED);
} }
Set<String> codes = new HashSet<>();
List<Element> concepts = cs.getChildrenByName("concept");
int i = 0;
for (Element concept : concepts) {
checkConcept(errors, cs, stack.push(concept, i, null, null), "true".equals(caseSensitive), hierarchyMeaning, csB, concept, codes);
i++;
}
return ok; return ok;
} }
private void checkConcept(List<ValidationMessage> errors, Element cs, NodeStack stack, boolean caseSensitive, String hierarchyMeaning, CodeSystem csB, Element concept, Set<String> codes) {
String code = concept.getNamedChildValue("code");
String display = concept.getNamedChildValue("display");
if (csB != null && !Utilities.noString(display)) {
ConceptDefinitionComponent b = CodeSystemUtilities.findCode(csB.getConcept(), code);
if (b != null && !b.getDisplay().equalsIgnoreCase(display)) {
String lang = cs.getNamedChildValue("language");
if ((lang == null && !csB.hasLanguage()) ||
csB.getLanguage().equals(lang)) {
// nothing new language wise, and the display doesn't match
hint(errors, "2024-03-06", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), false, I18nConstants.CODESYSTEM_SUPP_NO_DISPLAY, display, b.getDisplay(), lang == null? "undefined" : lang);
}
}
}
// todo: check that all the properties are defined.
// check that all the defined properties have values
// check the designations have values, and the use/language don't conflict
List<Element> concepts = concept.getChildrenByName("concept");
int i = 0;
for (Element child : concepts) {
checkConcept(errors, cs, stack.push(concept, i, null, null), caseSensitive, hierarchyMeaning, csB, child, codes);
i++;
}
}
private boolean checkShareableCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) { private boolean checkShareableCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
if (parent.isForPublication()) { if (parent.isForPublication()) {
if (isHL7(cs)) { if (isHL7(cs)) {
@ -138,6 +191,9 @@ public class CodeSystemValidator extends BaseValidator {
} }
private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement, int count, String supp) { private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement, int count, String supp) {
if (forPublication && (url.contains("hl7.org"))) {
hint(errors, "2024-03-07", IssueType.BUSINESSRULE, cs.line(), cs.col(), stack.getLiteralPath(), url.contains("terminology.hl7.org") || url.contains("hl7.org/cda/stds/core"), I18nConstants.CODESYSTEM_THO_CHECK);
}
if (isSupplement) { if (isSupplement) {
if (!"supplement".equals(content)) { if (!"supplement".equals(content)) {
NodeStack s = stack.push(cs.getNamedChild("content", false), -1, null, null); NodeStack s = stack.push(cs.getNamedChild("content", false), -1, null, null);