diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index eb879efea..c5e5892e5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -167,10 +167,12 @@ public class ValueSetCheckerSimple implements ValueSetChecker { } // also check to see if the value set has another display ConceptReferenceComponent vs = findValueSetRef(code.getSystem(), code.getCode()); - if (vs != null && vs.hasDisplay()) { - b.append(vs.getDisplay()); - if (code.getDisplay().equalsIgnoreCase(vs.getDisplay())) - return new ValidationResult(cc); + if (vs != null && (vs.hasDisplay() ||vs.hasDesignation())) { + if (vs.hasDisplay()) { + b.append(vs.getDisplay()); + if (code.getDisplay().equalsIgnoreCase(vs.getDisplay())) + return new ValidationResult(cc); + } for (ConceptReferenceDesignationComponent ds : vs.getDesignation()) { b.append(ds.getValue()); if (code.getDisplay().equalsIgnoreCase(ds.getValue())) @@ -188,7 +190,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker { if (system.equals(exp.getSystem()) && code.equals(exp.getCode())) { ConceptReferenceComponent cc = new ConceptReferenceComponent(); cc.setDisplay(exp.getDisplay()); - cc.setDesignation(cc.getDesignation()); + cc.setDesignation(exp.getDesignation()); return cc; } } @@ -382,6 +384,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker { private boolean codeInConceptFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) throws FHIRException { switch (f.getOp()) { case ISA: return codeInConceptIsAFilter(cs, f, code); + case ISNOTA: return !codeInConceptIsAFilter(cs, f, code); default: System.out.println("todo: handle concept filters with op = "+f.getOp()); throw new FHIRException("Unable to handle system "+cs.getUrl()+" concept filter with op = "+f.getOp()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java index 00f725187..6e904c796 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java @@ -821,14 +821,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } // public API - - private boolean checkCode(List errors, Element element, String path, String code, String system, String display) throws TerminologyServiceException { + private boolean checkCode(List errors, Element element, String path, String code, String system, String display, boolean checkDisplay) throws TerminologyServiceException { long t = System.nanoTime(); boolean ss = context.supportsSystem(system); txTime = txTime + (System.nanoTime() - t); if (ss) { t = System.nanoTime(); - ValidationResult s = context.validateCode(system, code, display); + ValidationResult s = context.validateCode(system, code, checkDisplay ? display : null); txTime = txTime + (System.nanoTime() - t); if (s == null) return true; @@ -936,7 +935,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - private void checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext) { + private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext) { + boolean res = true; if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing (cc)")) { @@ -969,6 +969,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // ignore this since we can't validate but it doesn't matter.. } else { ValidationResult vr = context.validateCode(cc, valueset); + res = false; if (!vr.isOk()) { bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { @@ -1023,6 +1024,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } + return res; } private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc) { @@ -1099,7 +1101,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), "userSelected", focus); } - private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept) { + private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay) { String code = element.getNamedChildValue("code"); String system = element.getNamedChildValue("system"); String display = element.getNamedChildValue("display"); @@ -1108,7 +1110,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (system != null && code != null && !noTerminologyChecks) { rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")"); try { - if (checkCode(errors, element, path, code, system, display)) + if (checkCode(errors, element, path, code, system, display, checkDisplay)) if (theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) { @@ -1237,7 +1239,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", allowedTypes.contains(actualType), "The Extension '" + url + "' definition allows for the types "+allowedTypes.toString()+" but found type "+actualType); // 3. is the content of the extension valid? - validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false); + validateElement(hostContext, errors, ex, ex.getSnapshot().getElement().get(0), null, null, resource, element, "Extension", stack, false, true); } return ex; @@ -2790,7 +2792,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(), "StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided"))) { // Don't need to validate against the resource if there's a profile because the profile snapshot will include the relevant parts of the resources - validateElement(hostContext, errors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false); + validateElement(hostContext, errors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true); } // specific known special validations @@ -2809,7 +2811,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // todo: re-enable this when I can deal with the impact... (GG) // if (!profileUsage.getProfile().getType().equals(resource.fhirType())) // throw new FHIRException("Profile type mismatch - resource is "+resource.fhirType()+", and profile is for "+profileUsage.getProfile().getType()); - validateElement(hostContext, errors, profileUsage.getProfile(), profileUsage.getProfile().getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false); + validateElement(hostContext, errors, profileUsage.getProfile(), profileUsage.getProfile().getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true); } } @@ -3632,7 +3634,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L // firstBase = ebase == null ? base : ebase; private void validateElement(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context, - Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept) throws FHIRException, FHIRException, IOException { + Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext) throws FHIRException, FHIRException, IOException { // element.markValidation(profile, definition); // System.out.println(" "+stack.getLiteralPath()+" "+Long.toString((System.nanoTime() - time) / 1000000)); @@ -3877,6 +3879,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L String eiPath = ei.path; assert(eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.path + " - localStack.getLiteralPath: " + localStackLiterapPath; boolean thisIsCodeableConcept = false; + boolean checkDisplay = true; ei.element.markValidation(profile, ei.definition); if (type != null) { @@ -3893,9 +3896,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L if (type.equals("Identifier")) { checkIdentifier(errors, ei.path, ei.element, ei.definition); } else if (type.equals("Coding")) { - checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept); + checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext); } else if (type.equals("CodeableConcept")) { - checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition); + checkDisplay = checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition); thisIsCodeableConcept = true; } else if (type.equals("Reference")) { checkReference(hostContext, errors, ei.path, ei.element, profile, ei.definition, actualType, localStack); @@ -3908,7 +3911,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L } } else { if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.name)) - validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, false); + validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, false, true); } StructureDefinition p = null; boolean elementValidated = false; @@ -3948,7 +3951,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L p = this.context.fetchResource(StructureDefinition.class, typeProfile); if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.path, p != null, "Unknown profile " + typeProfile)) { List profileErrors = new ArrayList(); - validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.element, type, localStack, thisIsCodeableConcept); + validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.element, type, localStack, thisIsCodeableConcept, checkDisplay); boolean hasError = false; for (ValidationMessage msg : profileErrors) { if (msg.getLevel()==ValidationMessage.IssueSeverity.ERROR || msg.getLevel()==ValidationMessage.IssueSeverity.FATAL) { @@ -3978,13 +3981,13 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L } if (p!=null) { if (!elementValidated) { - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.element, type, localStack, thisIsCodeableConcept); + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.element, type, localStack, thisIsCodeableConcept, checkDisplay); } int index = profile.getSnapshot().getElement().indexOf(ei.definition); if (index < profile.getSnapshot().getElement().size() - 1) { String nextPath = profile.getSnapshot().getElement().get(index+1).getPath(); if (!nextPath.equals(ei.definition.getPath()) && nextPath.startsWith(ei.definition.getPath())) - validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, thisIsCodeableConcept); + validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, thisIsCodeableConcept, checkDisplay); } } } diff --git a/org.hl7.fhir.validation/src/test/resources/validation-examples/manifest.json b/org.hl7.fhir.validation/src/test/resources/validation-examples/manifest.json index b73740357..754b49700 100644 --- a/org.hl7.fhir.validation/src/test/resources/validation-examples/manifest.json +++ b/org.hl7.fhir.validation/src/test/resources/validation-examples/manifest.json @@ -138,7 +138,7 @@ }, "patient-au-5.json" : { "errorCount": 0, - "warningCount": 0, + "warningCount": 1, "profile" : { "source" : "profile-patient-au.xml", "errorCount": 0 @@ -563,7 +563,7 @@ }, "patient-translated-codes.xml" : { "errorCount": 0, - "warningCount": 4, + "warningCount": 2, "profile" : { "source" : "patient-translated-codes.profile.xml", "errorCount": 0,