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 9d1c9dbba..fd093cecc 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 @@ -121,6 +121,7 @@ import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; +import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; @@ -872,15 +873,55 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; return false; } - - private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed) { + + + private boolean hasErrors(List errors) { + if (errors!=null) { + for (ValidationMessage vm : errors) { + if (vm.getLevel() == IssueSeverity.FATAL || vm.getLevel() == IssueSeverity.ERROR) { + return true; + } + } + } + return false; + } + + private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed, + boolean pattern) { checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), "text", focus); List codings = new ArrayList(); focus.getNamedChildren("coding", codings); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(), - "Expected " + Integer.toString(fixed.getCoding().size()) + " but found " + Integer.toString(codings.size()) + " coding elements")) { - for (int i = 0; i < codings.size(); i++) - checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), "coding", focus); + if (pattern) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() >= fixed.getCoding().size(), + "Expected " + Integer.toString(fixed.getCoding().size()) + " but found " + Integer.toString(codings.size()) + + " coding elements")) { + for (int i = 0; i < fixed.getCoding().size(); i++) { + Coding fixedCoding = fixed.getCoding().get(i); + boolean found = false; + List errorsFixed = null; + for (int j = 0; j < codings.size() && !found; ++j) { + errorsFixed = new ArrayList(); + checkFixedValue(errorsFixed, path + ".coding", codings.get(j), fixedCoding, "coding", focus); + if (!hasErrors(errorsFixed)) { + found = true; + } + } + if (!found) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, false, + "Expected patternCodeableConcept not found for"+ + " system: " + fixedCoding.getSystemElement().asStringValue() + + " code: " + fixedCoding.getCodeElement().asStringValue() + + " display: " + fixedCoding.getDisplayElement().asStringValue()); + } + } + } + } else { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(), + "Expected " + Integer.toString(fixed.getCoding().size()) + " but found " + Integer.toString(codings.size()) + + " coding elements")) { + for (int i = 0; i < codings.size(); i++) + checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), "coding", focus); + } } } @@ -1292,8 +1333,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // return b.toString(); // } // - + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String propName, Element parent) { + checkFixedValue(errors, path, focus, fixed, propName, parent, false); + } + + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String propName, Element parent, boolean pattern) { if ((fixed == null || fixed.isEmpty()) && focus == null) ; // this is all good else if (fixed == null && focus != null) @@ -1356,7 +1401,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (fixed instanceof HumanName) checkHumanName(errors, path, focus, (HumanName) fixed); else if (fixed instanceof CodeableConcept) - checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed); + checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed, pattern); else if (fixed instanceof Timing) checkTiming(errors, path, focus, (Timing) fixed); else if (fixed instanceof Period) @@ -1622,8 +1667,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - if (context.hasFixed()) - checkFixedValue(errors,path,e, context.getFixed(), context.getSliceName(), null); + if (context.hasFixed()) { + checkFixedValue(errors,path,e, context.getFixed(), context.getSliceName(), null, false); + } + if (context.hasPattern()) { + checkFixedValue(errors, path, e, context.getPattern(), context.getSliceName(), null, true); + } // for nothing to check } @@ -3823,6 +3872,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L if (ei.definition.hasFixed()) { checkFixedValue(errors,ei.path, ei.element, ei.definition.getFixed(), ei.definition.getSliceName(), null); } + if (ei.definition.hasPattern()) { + checkFixedValue(errors,ei.path, ei.element, ei.definition.getPattern(), ei.definition.getSliceName(), null, true); + } } if (type.equals("Identifier")) { checkIdentifier(errors, ei.path, ei.element, ei.definition); 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 a48d2cccc..cc6e652ad 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 @@ -489,6 +489,20 @@ "observation-cholesterol-bad-wrongcode.xml" : { "errorCount": 2, "errors": ["ERROR: Observation.code.coding.code: Value is '13457-7' but must be '35200-5'","ERROR: Observation.code.coding.display: Value is 'Cholesterol in LDL [Mass/volume] in Serum or Plasma by calculation' but must be 'Cholesterol [Moles/?volume] in Serum or Plasma'"] + }, + "observation-triglyceride-good.xml" : { + "warningCount": 1, + "errorCount": 0, + "warnings": ["WARNING: Observation.code.coding[1]: The display \"Triglyceride [Moles/​volume] in Serum or Plasma\" is not a valid display for the code {http://loinc.org}35217-9 - should be one of [\"Triglyceride [Mass or Moles/volume] in Serum or Plasma\",\"Trigl SerPl-msCnc\""] + }, + "observation-triglyceride-good2.xml" : { + "warningCount": 1, + "errorCount": 0, + "warnings": ["WARNING: Observation.code.coding[2]: The display \"Triglyceride [Moles/​volume] in Serum or Plasma\" is not a valid display for the code {http://loinc.org}35217-9 - should be one of [\"Triglyceride [Mass or Moles/volume] in Serum or Plasma\",\"Trigl SerPl-msCnc\""] + }, + "observation-triglyceride-bad-wrongcode.xml" : { + "errorCount": 1, + "errors": ["ERROR: Observation.code: Expected patternCodeableConcept not found for system: http://loinc.org code: 35217-9 display: Triglyceride [Moles/​volume] in Serum or Plasma"] } } } diff --git a/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-bad-wrongcode.xml b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-bad-wrongcode.xml new file mode 100644 index 000000000..247425206 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-bad-wrongcode.xml @@ -0,0 +1,103 @@ + + + + + + + + +
+

+ Generated Narrative with Details +

+

+ id + : triglyceride +

+

+ status + : final +

+

+ code + : Triglyceride + (Details : {LOINC code '35217-9' = 'Triglyceride [Mass or + Moles/volume] in Serum or Plasma', + given as 'Triglyceride + [Moles/​volume] in Serum or Plasma'}) + +

+

+ subject + : + Patient/pat2 +

+

+ performer + : + Acme Laboratory, Inc +

+

+ value + : 1.3 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +

+

ReferenceRanges

+ + + + + + + + + +
- + High +
* + 2.0 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good.xml b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good.xml new file mode 100644 index 000000000..6c4ccc179 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good.xml @@ -0,0 +1,103 @@ + + + + + + + + +
+

+ Generated Narrative with Details +

+

+ id + : triglyceride +

+

+ status + : final +

+

+ code + : Triglyceride + (Details : {LOINC code '35217-9' = 'Triglyceride [Mass or + Moles/volume] in Serum or Plasma', + given as 'Triglyceride + [Moles/​volume] in Serum or Plasma'}) + +

+

+ subject + : + Patient/pat2 +

+

+ performer + : + Acme Laboratory, Inc +

+

+ value + : 1.3 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +

+

ReferenceRanges

+ + + + + + + + + +
- + High +
* + 2.0 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good2.xml b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good2.xml new file mode 100644 index 000000000..87d0026a2 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/resources/validation-examples/observation-triglyceride-good2.xml @@ -0,0 +1,108 @@ + + + + + + + + +
+

+ Generated Narrative with Details +

+

+ id + : triglyceride +

+

+ status + : final +

+

+ code + : Triglyceride + (Details : {LOINC code '35217-9' = 'Triglyceride [Mass or + Moles/volume] in Serum or Plasma', + given as 'Triglyceride + [Moles/​volume] in Serum or Plasma'}) + +

+

+ subject + : + Patient/pat2 +

+

+ performer + : + Acme Laboratory, Inc +

+

+ value + : 1.3 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +

+

ReferenceRanges

+ + + + + + + + + +
- + High +
* + 2.0 mmol/L + (Details: UCUM code mmol/L = 'mmol/L') +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file