From c88d763bd890ee8c4ff467db8b5d462b564b958f Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 10:12:53 +0100 Subject: [PATCH 01/52] added missing closing quotation mark --- .../fhir/r5/validation/instancevalidator/InstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index fa8e4c459..d84037193 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -990,7 +990,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat CodeableConcept cc = convertToCodeableConcept(element, logical); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); From dc1c68e55a7ce11e6fa1da094641eea43f0a64f0 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 10:19:01 +0100 Subject: [PATCH 02/52] added another missing closing quotation mark --- .../fhir/r5/validation/instancevalidator/InstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index d84037193..83d1a4ad2 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -2570,7 +2570,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { if (isTransaction) { - rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref); + rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref + ")"); } else { rule(errors, IssueType.INVALID, -1, -1, path, false, "Relative URLs must be of the format [ResourceName]/[id]. Encountered " + ref); } From f8bda2174ba2b431d3829e128d64e85255596667 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 14:27:32 +0100 Subject: [PATCH 03/52] replaced escaped quotations with single quotes for better parsability --- .../instancevalidator/InstanceValidator.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index 83d1a4ad2..eb162d7b5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -1095,7 +1095,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); 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 + "\")"); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { @@ -1310,7 +1310,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); 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 + "\")"); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { @@ -1404,13 +1404,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xverManager.matchingUrl(url)) { switch (xverManager.status(url)) { case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion \"" + xverManager.getVersion(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion '" + xverManager.getVersion(url) + "')"); break; case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id \"" + xverManager.getElementId(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id '" + xverManager.getElementId(url) + "')"); break; case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id \"" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id '" + xverManager.getElementId(url) + "' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); break; case Valid: ex = xverManager.makeDefinition(url); @@ -1906,7 +1906,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (charCount > 0 && charCount % 4 != 0) { String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value \"{0}\" is not a valid Base64 value", value); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value '{0}' is not a valid Base64 value", value); } } } @@ -3307,7 +3307,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Gson gson = new Gson(); String json = gson.toJson((StringType) fixed); String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); + escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); expression.append("'" + escapedString + "'"); } else if (fixed instanceof UriType) { expression.append("'" + ((UriType) fixed).asStringValue() + "'"); @@ -3663,7 +3663,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \"" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire '" + questionnaire + "' could not be resolved, so no validation can be performed against the base questionnaire")) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } @@ -3872,7 +3872,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat NodeStack ns = stack.push(item, -1, null, null); validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire"); + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId '" + linkId + "' not found in questionnaire"); } else { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; @@ -5509,7 +5509,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); } catch (Exception e) { - System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " (\"" + inv.getExpression() + "\"): " + e.getMessage()); + System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); } } } From 393d7d24fb1abc76967082ddff3dd8a71ae90e13 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 14:37:10 +0100 Subject: [PATCH 04/52] added message properties file --- .../src/main/resources/Messages.properties | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 org.hl7.fhir.validation/src/main/resources/Messages.properties diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties new file mode 100644 index 000000000..398338220 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -0,0 +1,221 @@ +The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Expected__but_found__line_elements = Expected {0} but found {1} line elements +Unknown_Code_System_ = Unknown Code System '{0}' +Unknown_Code_ = Unknown Code ({0}#{1}) +Display_should_be_ = Display should be '{0}' +Invalid_System_URI_ = Invalid System URI: {0} +Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system = Invalid System URI: {0} - cannot use a value set URI as a system +Code_System_URI__is_unknown_so_the_code_cannot_be_validated = Code System URI '{0}' is unknown so the code cannot be validated +Expected__but_found__coding_elements = Expected {0} but found {1} coding elements +Binding_for__missing_cc = Binding for {0} missing (cc) +ValueSet__not_found_by_validator = ValueSet {0} not found by validator +No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_ = No code provided, and a code must be provided from the value set {0} (max value set {1}) +No_code_provided_and_a_code_should_be_provided_from_the_value_set__ = No code provided, and a code should be provided from the value set {0} ({1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__ = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__ = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__ = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__ = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__ = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Error__validating_CodeableConcept = Error {0} validating CodeableConcept +Binding_by_URI_reference_cannot_be_checked = Binding by URI reference cannot be checked +Binding_for_path__has_no_source_so_cant_be_checked = Binding for path {0} has no source, so can't be checked +No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) +Code_0_is_not_a_valid_code_in_code_system_1 = Code {0} is not a valid code in code system {1} +Codingsystem_must_be_an_absolute_reference_not_a_local_reference = Coding.system must be an absolute reference, not a local reference +The_Coding_references_a_value_set_not_a_code_system_ = The Coding references a value set, not a code system ('{0}') +Binding_for__missing = Binding for {0} missing +The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided could not be validated in the absence of a terminology server +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Error__validating_Coding = Error {0} validating Coding +Error__validating_Coding_ = Error {0} validating Coding: {1} +None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__ = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +Error__validating_CodeableConcept_using_maxValueSet = Error {0} validating CodeableConcept using maxValueSet +The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__ = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_ = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Extension_url__is_not_valid_invalidVersion_ = Extension url '{0}' is not valid (invalidVersion '{1}') +Extension_url__is_not_valid_unknown_Element_id_ = Extension url '{0}' is not valid (unknown Element id '{1}') +Extension_url__is_not_valid_Element_id__is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_url__evaluation_state_illegal = Extension url '{0}' evaluation state illegal +Subextension_url__is_not_defined_by_the_Extension_ = Sub-extension url '{0}' is not defined by the Extension {1} +The_extension__is_unknown_and_not_allowed_here = The extension {0} is unknown, and not allowed here +Unknown_extension_ = Unknown extension {0} +Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is +The_Extension__must_be_used_as_a_modifierExtension = The Extension '{0}' must be used as a modifierExtension +The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +The_Extension__definition_allows_for_the_types__but_found_type_ = The Extension '{0}' definition allows for the types {1} but found type {2} +The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_ = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) +The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_ = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Missing_element___required_by_fixed_value_assigned_in_profile_ = Missing element '{0}' - required by fixed value assigned in profile {1} +Value_is__but_must_be_ = Value is '{0}' but must be '{1}' +Unhandled_fixed_value_type_ = Unhandled fixed value type {0} +No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions = No extensions allowed, as the specified fixed value doesn't contain any extensions +Extensions_count_mismatch_expected__but_found_ = Extensions count mismatch: expected {0} but found {1} +Extension_count_mismatch_unable_to_find_extension_ = Extension count mismatch: unable to find extension: {0} +Expected__but_found__family_elements = Expected {0} but found {1} family elements +Expected__but_found__given_elements = Expected {0} but found {1} given elements +Expected__but_found__prefix_elements = Expected {0} but found {1} prefix elements +Expected__but_found__suffix_elements = Expected {0} but found {1} suffix elements +Identifiersystem_must_be_an_absolute_reference_not_a_local_reference = Identifier.system must be an absolute reference, not a local reference +Primitive_types_must_have_a_value_or_must_have_child_extensions = Primitive types must have a value or must have child extensions +Primitive_types_must_have_a_value_that_is_not_empty = Primitive types must have a value that is not empty +Primitive_types_should_not_only_be_whitespace = Primitive types should not only be whitespace +Element_value__does_not_meet_regex_ = Element value '{0}' does not meet regex '{1}' +boolean_values_must_be_true_or_false = boolean values must be 'true' or 'false' +URI_values_cannot_start_with_oid = URI values cannot start with oid: +URI_values_cannot_start_with_uuid = URI values cannot start with uuid: +URI_values_cannot_have_whitespace = URI values cannot have whitespace('{0}') +value_is_longer_than_permitted_maximum_length_of_ = value is longer than permitted maximum length of {0} +OIDs_must_start_with_urnoid = OIDs must start with urn:oid: +OIDs_must_be_valid = OIDs must be valid +UUIDs_must_start_with_urnuuid = UUIDs must start with urn:uuid: +UUIDs_must_be_valid_ = UUIDs must be valid ({0}) +URL_value__does_not_resolve = URL value '{0}' does not resolve +id_value__is_not_valid = id value '{0}' is not valid +value_cannot_be_empty = @value cannot be empty +value_should_not_start_or_finish_with_whitespace = value should not start or finish with whitespace +value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes = value is longer than permitted maximum length of 1 MB (1048576 bytes) +The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error = The value '{0}' is outside the range of reasonable years - check for data entry error +Not_a_valid_date_time = Not a valid date time +if_a_date_has_a_time_it_must_have_a_timezone = if a date has a time, it must have a timezone +Not_a_valid_datetime_ = Not a valid date/time ({0}) +Not_a_valid_time = Not a valid time +Not_a_valid_time_ = Not a valid time ({0}) +Not_a_valid_date = Not a valid date +value_is_longer_than_permitted_maximum_value_of_ = value is longer than permitted maximum value of {0} +Not_a_valid_date_ = Not a valid date ({0}) +The_value_0_is_not_a_valid_Base64_value = The value '{0}' is not a valid Base64 value +The_value__is_not_a_valid_integer = The value '{0}' is not a valid integer +value_is_greater_than_permitted_maximum_value_of_ = value is greater than permitted maximum value of {0} +value_is_less_than_permitted_minimum_value_of_ = value is less than permitted minimum value of {0} +value_is_less_than_permitted_minimum_value_of_0 = value is less than permitted minimum value of 0 +value_is_less_than_permitted_minimum_value_of_1 = value is less than permitted minimum value of 1 +The_value__is_not_a_valid_integer64 = The value '{0}' is not a valid integer64 +The_value__is_not_a_valid_decimal = The value '{0}' is not a valid decimal +The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals = The value '{0}' is outside the range of commonly/reasonably supported decimals +The_instant__is_not_valid_by_regex = The instant '{0}' is not valid (by regex) +Not_a_valid_instant_ = Not a valid instant ({0}) +The_code__is_not_valid_whitespace_rules = The code '{0}' is not valid (whitespace rules) +Wrong_namespace_on_the_XHTML__should_be_ = Wrong namespace on the XHTML ('{0}', should be '{1}') +Wrong_name_on_the_XHTML___must_start_with_div = Wrong name on the XHTML ('{0}') - must start with div +Illegal_element_name_in_the_XHTML_ = Illegal element name in the XHTML ('{0}') +Illegal_attribute_name_in_the_XHTML__on_ = Illegal attribute name in the XHTML ('{0}' on '{1}') +ValueSet_0_not_found_by_validator = ValueSet {0} not found by validator +The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided ('{0}') could not be validated in the absence of a terminology server +The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Binding_has_no_source_so_cant_be_checked = Binding has no source, so can't be checked +A_Reference_without_an_actual_reference_or_identifier_should_have_a_display = A Reference without an actual reference or identifier should have a display +Unable_to_resolve_resource_ = Unable to resolve resource '{0}' +The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' is not a valid Target for this element (must be one of {1}) +The_specified_type__does_not_match_the_found_type_ = The specified type '{0}' does not match the found type '{1}' +Unable_to_determine_type_of_target_resource = Unable to determine type of target resource +Unable_to_resolve_the_profile_reference_ = Unable to resolve the profile reference '{0}' +Unable_to_find_matching_profile_for__by_type_among_choices_ = Unable to find matching profile for {0} (by type) among choices: {1} +Unable_to_find_matching_profile_for__among_choices_ = Unable to find matching profile for {0} among choices: {1} +Found_multiple_matching_profiles_for__among_choices_ = Found multiple matching profiles for {0} among choices: {1} +Invalid_Resource_target_type_Found__but_expected_one_of_ = Invalid Resource target type. Found {0}, but expected one of ({1}) +Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Bundled_or_contained_reference_not_found_within_the_bundleresource_ = Bundled or contained reference not found within the bundle/resource {0} +The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' implied by the reference URL {1} is not a valid Target for this element (must be one of {2}) +Expected__but_found__event_elements = Expected {0} but found {1} event elements +Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_ = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) +Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_ = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Multiple_matches_in_bundle_for_reference_ = Multiple matches in bundle for reference {0} +Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references +Matching_reference_for_reference__has_resourceType_ = Matching reference for reference {0} has resourceType {1} +URN_reference_is_not_locally_contained_within_the_bundle_ = URN reference is not locally contained within the bundle {0} +Bundle_entry_missing_fullUrl = Bundle entry missing fullUrl +Profile_reference__could_not_be_resolved_so_has_not_been_checked = Profile reference '{0}' could not be resolved, so has not been checked +StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided +Questions_with_an_enableWhen_must_have_a_value_for_the_question_link = Questions with an enableWhen must have a value for the question link +Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Unable_to_find_target__for_this_question_enableWhen = Unable to find target '{0}' for this question enableWhen +Target_for_this_question_enableWhen_cant_reference_itself = Target for this question enableWhen can't reference itself +The_target_of_this_enableWhen_rule__comes_after_the_question_itself = The target of this enableWhen rule ({0}) comes after the question itself +Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_ = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_ = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Duplicate_Security_Label_ = Duplicate Security Label {0} +Type_mismatch__SearchParameter__type_is__but_type_here_is_ = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_ = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire = No questionnaire is identified, so no validation can be performed against the base questionnaire +The_questionnaire__could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_ = If text exists, it must match the questionnaire definition for linkId {0} +No_response_answer_found_for_required_item_ = No response answer found for required item {0} +Item_has_answer_2_even_though_it_is_not_enabled_ = Item has answer (2), even though it is not enabled {0} +Only_one_response_answer_item_with_this_linkId_allowed = Only one response answer item with this linkId allowed +Items_of_type_group_should_not_have_answers = Items of type group should not have answers +Definition_for_item__does_not_contain_a_type = Definition for item {0} does not contain a type +Items_not_of_type_DISPLAY_should_not_have_items__linkId_0 = Items not of type DISPLAY should not have items - linkId {0} +Only_one_response_item_with_this_linkId_allowed__ = Only one response item with this linkId allowed - {0} +No_LinkId_so_cant_be_validated = No LinkId, so can't be validated +LinkId__not_found_in_questionnaire = LinkId '{0}' not found in questionnaire +Structural_Error_items_are_out_of_order = Structural Error: items are out of order +Item_has_answer_even_though_it_is_not_enabled_item_id__ = Item has answer, even though it is not enabled (item id = '{0}') +Answer_value_must_be_of_type_ = Answer value must be of type {0} +Answer_value_must_be_one_of_the_types_ = Answer value must be one of the types {0} +Error__validating_Coding_against_Questionnaire_Options = Error {0} validating Coding against Questionnaire Options +Cannot_validate_options_because_no_option_or_options_are_provided = Cannot validate options because no option or options are provided +Option_list_has_no_option_values_of_type_integer = Option list has no option values of type integer +The_integer__is_not_a_valid_option = The integer {0} is not a valid option +Cannot_validate_integer_answer_option_because_no_option_list_is_provided = Cannot validate integer answer option because no option list is provided +Option_list_has_no_option_values_of_type_date = Option list has no option values of type date +The_date__is_not_a_valid_option = The date {0} is not a valid option +Cannot_validate_date_answer_option_because_no_option_list_is_provided = Cannot validate date answer option because no option list is provided +Option_list_has_no_option_values_of_type_time = Option list has no option values of type time +The_time__is_not_a_valid_option = The time {0} is not a valid option +Cannot_validate_time_answer_option_because_no_option_list_is_provided = Cannot validate time answer option because no option list is provided +Option_list_has_no_option_values_of_type_string = Option list has no option values of type string +The_string__is_not_a_valid_option = The string {0} is not a valid option +Cannot_validate_string_answer_option_because_no_option_list_is_provided = Cannot validate string answer option because no option list is provided +Option_list_has_no_option_values_of_type_coding = Option list has no option values of type coding +The_code__is_not_a_valid_option = The code {0}::{1} is not a valid option +Cannot_validate_Coding_option_because_no_option_list_is_provided = Cannot validate Coding option because no option list is provided +Documents_or_Messages_must_contain_at_least_one_entry = Documents or Messages must contain at least one entry +No_resource_on_first_entry = No resource on first entry +The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself +Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__ = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Entry__isnt_reachable_by_traversing_from_first_Bundle_entry = Entry {0} isn't reachable by traversing from first Bundle entry +Cant_find__in_the_bundle_ = Can't find '{0}' in the bundle ({1}) +The_type__is_not_valid__no_resources_allowed_here = The type '{0} is not valid - no resources allowed here +No_profile_found_for_contained_resource_of_type_ = No profile found for contained resource of type '{0}' +The_type__is_not_valid__must_be_ = The type '{0}' is not valid - must be {1} +The_type__is_not_valid__must_be_one_of_ = The type '{0}' is not valid - must be one of {1} +The_first_entry_in_a_document_must_be_a_composition = The first entry in a document must be a composition +The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_ = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Extensionurl_is_required = Extension.url is required +Extensionurl_must_be_an_absolute_URL = Extension.url must be an absolute URL +Unrecognised_Content_ = Unrecognised Content {0} +Unknown_type_ = Unknown type {0} +Unknown_profile_ = Unknown profile {0} +Unable_to_find_matching_profile_among_choices_ = Unable to find matching profile among choices: {0} +Found_multiple_matching_profiles_among_choices_ = Found multiple matching profiles among choices: {0} +_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +_minimum_required___but_only_found_ = {0}: minimum required = {1}, but only found {2} +_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +_max_allowed___but_found_ = {0}: max allowed = {1}, but found {2} +This_element_does_not_match_any_known_slice_ = This element does not match any known slice {0} +This_element_is_not_allowed_by_the_profile_ = This element is not allowed by the profile {0} +As_specified_by_profile__Element__is_out_of_order = As specified by profile {0}, Element '{1}' is out of order +As_specified_by_profile__Element__is_out_of_order_in_ordered_slice = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Profile__Element_matches_more_than_one_slice___ = Profile {0}, Element matches more than one slice - {1}, {2} +The_first_entry_in_a_message_must_be_a_MessageHeader = The first entry in a message must be a MessageHeader +No_definition_found_for_resource_type_ = No definition found for resource type '{0}' +Specified_profile_type_was__but_found_type_ = Specified profile type was '{0}', but found type '{1}' +Resource_requires_an_id_but_none_is_present = Resource requires an id, but none is present +Resource_has_an_id_but_none_is_allowed = Resource has an id, but none is allowed From 2814394566fc5023c968f1914902473bf753f23b Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 14:45:58 +0100 Subject: [PATCH 05/52] externalized InstanceValidator String txRule missing, still some bugs --- .../instancevalidator/InstanceValidator.java | 10335 ++++++++-------- 1 file changed, 5141 insertions(+), 5194 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index eb162d7b5..490c4d592 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -32,7 +32,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; @@ -167,5421 +169,5366 @@ import ca.uhn.fhir.util.ObjectUtil; public class InstanceValidator extends BaseValidator implements IResourceValidator { - private class ValidatorHostServices implements IEvaluationContext { - - @Override - public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (externalHostServices != null) - return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); - else - return null; - } - - @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (externalHostServices != null) - return externalHostServices.resolveConstantType(c.getAppContext(), name); - else - return null; - } - - @Override - public boolean log(String argument, List focus) { - if (externalHostServices != null) - return externalHostServices.log(argument, focus); - else - return false; - } - - @Override - public FunctionDetails resolveFunction(String functionName) { - throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); - } - - @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); - } - - @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { - throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); - } - - @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - - if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { - return (Base) refContext.getUserData("validator.bundle.resolution"); - } - - if (c.getAppContext() instanceof Element) { - Element bnd = (Element) c.getAppContext(); - Base res = resolveInBundle(url, bnd); - if (res != null) - return res; - } - Base res = resolveInBundle(url, c.getResource()); - if (res != null) - return res; - res = resolveInBundle(url, c.getContainer()); - if (res != null) - return res; - - if (externalHostServices != null) - return externalHostServices.resolveReference(c.getAppContext(), url, refContext); - else if (fetcher != null) - try { - return fetcher.fetch(c.getAppContext(), url); - } catch (IOException e) { - throw new FHIRException(e); - } - else - throw new Error("Not done yet - resolve " + url + " locally (2)"); - - } - - public Base resolveInBundle(String url, Element bnd) { - if (bnd == null) - return null; - if (bnd.fhirType().equals("Bundle")) { - for (Element be : bnd.getChildrenByName("entry")) { - Element res = be.getNamedChild("resource"); - if (res != null) { - String fullUrl = be.getChildValue("fullUrl"); - String rt = res.fhirType(); - String id = res.getChildValue("id"); - if (url.equals(fullUrl)) - return res; - if (url.equals(rt + "/" + id)) - return res; - } - } - } - return null; - } - - @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { - ValidatorHostContext ctxt = (ValidatorHostContext) appContext; - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - if (sd == null) { - throw new FHIRException("Unable to resolve " + url); - } - InstanceValidator self = InstanceValidator.this; - List valerrors = new ArrayList(); - if (item instanceof Resource) { - try { - Element e = new ObjectConverter(context).convert((Resource) item); - self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); - } catch (IOException e1) { - throw new FHIRException(e1); - } - } else if (item instanceof Element) { - Element e = (Element) item; - if (e.isResource()) { - self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); - } else { - throw new FHIRException("Not supported yet"); - } - } else - throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); - boolean ok = true; - List record = new ArrayList<>(); - for (ValidationMessage v : valerrors) { - ok = ok && !v.getLevel().isError(); - if (v.getLevel().isError() || v.isSlicingHint()) { - record.add(v); - } - } - if (!ok && !record.isEmpty()) { - ctxt.sliceNotes(url, record); - } - return ok; - } - - @Override - public ValueSet resolveValueSet(Object appContext, String url) { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (c.getProfile() != null && url.startsWith("#")) { - for (Resource r : c.getProfile().getContained()) { - if (r.getId().equals(url.substring(1))) { - if (r instanceof ValueSet) - return (ValueSet) r; - else - throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); - } - } - return null; - } - return context.fetchResource(ValueSet.class, url); - } - - } - - private IWorkerContext context; - private FHIRPathEngine fpe; - - // configuration items - private CheckDisplayOption checkDisplay; - private boolean anyExtensionsAllowed; - private boolean errorForUnknownProfiles; - private boolean noInvariantChecks; - private boolean noTerminologyChecks; - private boolean hintAboutNonMustSupport; - private boolean showMessagesFromReferences; - private BestPracticeWarningLevel bpWarnings; - private String validationLanguage; - private boolean baseOnly; - - private List extensionDomains = new ArrayList(); - - private IdStatus resourceIdRule; - private boolean allowXsiLocation; - - // used during the build process to keep the overall volume of messages down - private boolean suppressLoincSnomedMessages; - - // time tracking - private long overall = 0; - private long txTime = 0; - private long sdTime = 0; - private long loadTime = 0; - private long fpeTime = 0; - - private boolean noBindingMsgSuppressed; - private boolean debug; - private Map fetchCache = new HashMap<>(); - private HashMap resourceTracker = new HashMap<>(); - private IValidatorResourceFetcher fetcher; - long time = 0; - private IEvaluationContext externalHostServices; - private boolean noExtensibleWarnings; - private String serverBase; - - private EnableWhenEvaluator myEnableWhenEvaluator = new EnableWhenEvaluator(); - private String executionId; - private XVerExtensionManager xverManager; - private IValidationProfileUsageTracker tracker; - private ValidatorHostServices validatorServices; - private boolean assumeValidRestReferences; - private boolean allowExamples; - - public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { - super(); - this.context = theContext; - this.externalHostServices = hostServices; - fpe = new FHIRPathEngine(context); - validatorServices = new ValidatorHostServices(); - fpe.setHostServices(validatorServices); - if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0")) - fpe.setLegacyMode(true); - source = Source.InstanceValidator; - } + private class ValidatorHostServices implements IEvaluationContext { @Override - public boolean isNoExtensibleWarnings() { - return noExtensibleWarnings; - } - - @Override - public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings) { - this.noExtensibleWarnings = noExtensibleWarnings; - return this; - } - - @Override - public boolean isShowMessagesFromReferences() { - return showMessagesFromReferences; - } - - @Override - public void setShowMessagesFromReferences(boolean showMessagesFromReferences) { - this.showMessagesFromReferences = showMessagesFromReferences; - } - - @Override - public boolean isNoInvariantChecks() { - return noInvariantChecks; - } - - @Override - public IResourceValidator setNoInvariantChecks(boolean value) { - this.noInvariantChecks = value; - return this; - } - - public IValidatorResourceFetcher getFetcher() { - return this.fetcher; - } - - public IResourceValidator setFetcher(IValidatorResourceFetcher value) { - this.fetcher = value; - return this; - } - - public IValidationProfileUsageTracker getTracker() { - return this.tracker; - } - - public IResourceValidator setTracker(IValidationProfileUsageTracker value) { - this.tracker = value; - return this; - } - - - public boolean isHintAboutNonMustSupport() { - return hintAboutNonMustSupport; - } - - public void setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) { - this.hintAboutNonMustSupport = hintAboutNonMustSupport; - } - - public boolean isAssumeValidRestReferences() { - return this.assumeValidRestReferences; - } - - public void setAssumeValidRestReferences(boolean value) { - this.assumeValidRestReferences = value; - } - - public boolean isAllowExamples() { - return this.allowExamples; - } - - public void setAllowExamples(boolean value) { - this.allowExamples = value; - } - - - private boolean allowUnknownExtension(String url) { - if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression")) - // Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with - return true; - for (String s : extensionDomains) - if (url.startsWith(s)) - return true; - return anyExtensionsAllowed; - } - - private boolean isKnownExtension(String url) { - // Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with - if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION)) - return true; - for (String s : extensionDomains) - if (url.startsWith(s)) - return true; - return false; - } - - private void bpCheck(List errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) { - if (bpWarnings != null) { - switch (bpWarnings) { - case Error: - rule(errors, invalid, line, col, literalPath, test, message); - break; - case Warning: - warning(errors, invalid, line, col, literalPath, test, message); - break; - case Hint: - hint(errors, invalid, line, col, literalPath, test, message); - break; - default: // do nothing - break; - } - } - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format) throws FHIRException { - return validate(appContext, errors, stream, format, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, stream, format, profiles); - } - - private StructureDefinition getSpecifiedProfile(String profile) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); - if (sd == null) { - throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); - } - return sd; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, List profiles) throws FHIRException { - ParserBase parser = Manager.makeParser(context, format); - if (parser instanceof XmlParser) - ((XmlParser) parser).setAllowXsiLocation(allowXsiLocation); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(stream); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource) throws FHIRException { - return validate(appContext, errors, resource, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, resource, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, List profiles) throws FHIRException { - long t = System.nanoTime(); - Element e; - try { - e = new ObjectConverter(context).convert(resource); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element) throws FHIRException { - return validate(appContext, errors, element, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, element, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, List profiles) throws FHIRException { - XmlParser parser = new XmlParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(element); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document) throws FHIRException { - return validate(appContext, errors, document, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, document, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, List profiles) throws FHIRException { - XmlParser parser = new XmlParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(document); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object) throws FHIRException { - return validate(appContext, errors, object, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, object, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, List profiles) throws FHIRException { - JsonParser parser = new JsonParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e = parser.parse(object); - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public void validate(Object appContext, List errors, Element element) throws FHIRException { - validate(appContext, errors, element, new ArrayList<>()); - } - - @Override - public void validate(Object appContext, List errors, Element element, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - validate(appContext, errors, element, profiles); - } - - @Override - public void validate(Object appContext, List errors, Element element, List profiles) throws FHIRException { - // this is the main entry point; all the other public entry points end up here coming here... - // so the first thing to do is to clear the internal state - fetchCache.clear(); - fetchCache.put(element.fhirType() + "/" + element.getIdBase(), element); - resourceTracker.clear(); - executionId = UUID.randomUUID().toString(); - baseOnly = profiles.isEmpty(); - - long t = System.nanoTime(); - if (profiles == null || profiles.isEmpty()) { - validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(element)); - } else { - for (StructureDefinition defn : profiles) { - validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(element)); - } - } - if (hintAboutNonMustSupport) { - checkElementUsage(errors, element, new NodeStack(element)); - } - overall = System.nanoTime() - t; - } - - private void checkElementUsage(List errors, Element element, NodeStack stack) { - String elementUsage = element.getUserString("elementSupported"); - hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), - "The element " + element.getName() + " is not marked as 'mustSupport' in the profile " + element.getProperty().getStructure().getUrl() + ". Consider not using the element, or marking the element as must-Support in the profile"); - - if (element.hasChildren()) { - String prevName = ""; - int elementCount = 0; - for (Element ce : element.getChildren()) { - if (ce.getName().equals(prevName)) - elementCount++; - else { - elementCount = 1; - prevName = ce.getName(); - } - checkElementUsage(errors, ce, stack.push(ce, elementCount, null, null)); - } - } - } - - private boolean check(String v1, String v2) { - return v1 == null ? Utilities.noString(v1) : v1.equals(v2); - } - - private void checkAddress(List errors, String path, Element focus, Address fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - checkFixedValue(errors, path + ".city", focus.getNamedChild("city"), fixed.getCityElement(), fixedSource, "city", focus, pattern); - checkFixedValue(errors, path + ".state", focus.getNamedChild("state"), fixed.getStateElement(), fixedSource, "state", focus, pattern); - checkFixedValue(errors, path + ".country", focus.getNamedChild("country"), fixed.getCountryElement(), fixedSource, "country", focus, pattern); - checkFixedValue(errors, path + ".zip", focus.getNamedChild("zip"), fixed.getPostalCodeElement(), fixedSource, "postalCode", focus, pattern); - - List lines = new ArrayList(); - focus.getNamedChildren("line", lines); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(), - "Expected " + Integer.toString(fixed.getLine().size()) + " but found " + Integer.toString(lines.size()) + " line elements")) { - for (int i = 0; i < lines.size(); i++) - checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); - } - } - - private void checkAttachment(List errors, String path, Element focus, Attachment fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".contentType", focus.getNamedChild("contentType"), fixed.getContentTypeElement(), fixedSource, "contentType", focus, pattern); - checkFixedValue(errors, path + ".language", focus.getNamedChild("language"), fixed.getLanguageElement(), fixedSource, "language", focus, pattern); - checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); - checkFixedValue(errors, path + ".url", focus.getNamedChild("url"), fixed.getUrlElement(), fixedSource, "url", focus, pattern); - checkFixedValue(errors, path + ".size", focus.getNamedChild("size"), fixed.getSizeElement(), fixedSource, "size", focus, pattern); - checkFixedValue(errors, path + ".hash", focus.getNamedChild("hash"), fixed.getHashElement(), fixedSource, "hash", focus, pattern); - checkFixedValue(errors, path + ".title", focus.getNamedChild("title"), fixed.getTitleElement(), fixedSource, "title", focus, pattern); - } - - // public API - private boolean checkCode(List errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) 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(new ValidationOptions(stack.workingLang), system, code, checkDisplay ? display : null); - txTime = txTime + (System.nanoTime() - t); - if (s == null) - return true; - if (s.isOk()) { - if (s.getMessage() != null) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - return true; - } - if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else if (s.getSeverity() == IssueSeverity.INFORMATION) - txHint(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else if (s.getSeverity() == IssueSeverity.WARNING) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else - return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage() + " for '" + system + "#" + code + "'"); - return true; - } else if (system.startsWith("http://hl7.org/fhir")) { - if (Utilities.existsInList(system, "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/srt")) - return true; // else don't check these (for now) - else if (system.startsWith("http://hl7.org/fhir/test")) - return true; // we don't validate these - else { - CodeSystem cs = getCodeSystem(system); - if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System '" + system + "'")) { - ConceptDefinitionComponent def = getCodeDefinition(cs, code); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")")) - return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'"); - } - return false; - } - } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - return true; // no checks in this case - } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system); - return false; - } else { - try { - if (context.fetchResourceWithException(ValueSet.class, system) != null) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system + " - cannot use a value set URI as a system"); - // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. - } - hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated"); - return true; - } catch (Exception e) { - return true; - } - } - } - - private boolean startsWithButIsNot(String system, String... uri) { - for (String s : uri) - if (!system.equals(s) && system.startsWith(s)) - return true; - return false; - } - - - 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, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - List codings = new ArrayList(); - focus.getNamedChildren("coding", codings); - 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 allErrorsFixed = new ArrayList<>(); - List errorsFixed; - for (int j = 0; j < codings.size() && !found; ++j) { - errorsFixed = new ArrayList<>(); - checkFixedValue(errorsFixed, path + ".coding", codings.get(j), fixedCoding, fixedSource, "coding", focus, pattern); - if (!hasErrors(errorsFixed)) { - found = true; - } else { - errorsFixed - .stream() - .filter(t -> t.getLevel().ordinal() >= IssueSeverity.ERROR.ordinal()) - .forEach(t -> allErrorsFixed.add(t)); - } - } - if (!found) { - // The argonaut DSTU2 labs profile requires userSelected=false on the category.coding and this - // needs to produce an understandable error message - String message = "Expected CodeableConcept " + (pattern ? "pattern" : "fixed value") + " not found for" + - " system: " + fixedCoding.getSystemElement().asStringValue() + - " code: " + fixedCoding.getCodeElement().asStringValue() + - " display: " + fixedCoding.getDisplayElement().asStringValue(); - if (fixedCoding.hasUserSelected()) { - message += " userSelected: " + fixedCoding.getUserSelected(); - } - message += " - Issues: " + allErrorsFixed; - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, false, message); - } - } - } - } 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), fixedSource, "coding", focus); - } - } - } - - private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) { - 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)")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); - else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); - } - } else { - long t = System.nanoTime(); - - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); - 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"), cc, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - 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"), cc, stack); - if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); - } - } - } - } else if (vr.getMessage() != null) { - res = false; - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - res = false; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).noCheckValueSetMembership(), nextCoding, valueset); - if (vr.getSeverity() != null) { - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else if (vr.getSeverity() == IssueSeverity.WARNING) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } - } - } - } - } - txTime = txTime + (System.nanoTime() - t); - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - return res; - } - - private boolean checkTerminologyCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { - 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)")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - CodeableConcept cc = convertToCodeableConcept(element, logical); - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); - else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); - } - } else { - long t = System.nanoTime(); - - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); - 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"), cc, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - 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"), cc, stack); - if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); - } - } - } - } else if (vr.getMessage() != null) { - res = false; - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - res = false; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - String nextCode = nextCoding.getCode(); - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); - if (!vr.isOk()) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); - } - } - } - } - txTime = txTime + (System.nanoTime() - t); - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); - } - // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. - if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { - checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - return res; - } - - private void checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { - Coding c = convertToCoding(element, logical); - String code = c.getCode(); - String system = c.getSystem(); - String display = c.getDisplay(); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); - - 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, checkDisplay, stack)) - 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")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); - else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); - 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"), c, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - 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"), c, stack); - else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - } - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); - } - } - } - - private CodeableConcept convertToCodeableConcept(Element element, StructureDefinition logical) { - CodeableConcept res = new CodeableConcept(); - for (ElementDefinition ed : logical.getSnapshot().getElement()) { - if (Utilities.charCount(ed.getPath(), '.') == 1) { - List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); - for (String m : maps) { - String name = tail(ed.getPath()); - List list = new ArrayList<>(); - element.getNamedChildren(name, list); - if (!list.isEmpty()) { - if ("Coding.code".equals(m)) { - res.getCodingFirstRep().setCode(list.get(0).primitiveValue()); - } else if ("Coding.system[fmt:OID]".equals(m)) { - String oid = list.get(0).primitiveValue(); - String url = context.oid2Uri(oid); - if (url != null) { - res.getCodingFirstRep().setSystem(url); - } else { - res.getCodingFirstRep().setSystem("urn:oid:" + oid); - } - } else if ("Coding.version".equals(m)) { - res.getCodingFirstRep().setVersion(list.get(0).primitiveValue()); - } else if ("Coding.display".equals(m)) { - res.getCodingFirstRep().setDisplay(list.get(0).primitiveValue()); - } else if ("CodeableConcept.text".equals(m)) { - res.setText(list.get(0).primitiveValue()); - } else if ("CodeableConcept.coding".equals(m)) { - StructureDefinition c = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode()); - for (Element e : list) { - res.addCoding(convertToCoding(e, c)); - } - } - } - } - } - } - return res; - } - - private Coding convertToCoding(Element element, StructureDefinition logical) { - Coding res = new Coding(); - for (ElementDefinition ed : logical.getSnapshot().getElement()) { - if (Utilities.charCount(ed.getPath(), '.') == 1) { - List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); - for (String m : maps) { - String name = tail(ed.getPath()); - List list = new ArrayList<>(); - element.getNamedChildren(name, list); - if (!list.isEmpty()) { - if ("Coding.code".equals(m)) { - res.setCode(list.get(0).primitiveValue()); - } else if ("Coding.system[fmt:OID]".equals(m)) { - String oid = list.get(0).primitiveValue(); - String url = context.oid2Uri(oid); - if (url != null) { - res.setSystem(url); - } else { - res.setSystem("urn:oid:" + oid); - } - } else if ("Coding.version".equals(m)) { - res.setVersion(list.get(0).primitiveValue()); - } else if ("Coding.display".equals(m)) { - res.setDisplay(list.get(0).primitiveValue()); - } - } - } - } - } - return res; - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided could be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (code = " + c.getSystem() + "#" + c.getCode() + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private String ccSummary(CodeableConcept cc) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (Coding c : cc.getCoding()) - b.append(c.getSystem() + "#" + c.getCode()); - return b.toString(); - } - - private void checkCoding(List errors, String path, Element focus, Coding fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".version", focus.getNamedChild("version"), fixed.getVersionElement(), fixedSource, "version", focus, pattern); - checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); - checkFixedValue(errors, path + ".display", focus.getNamedChild("display"), fixed.getDisplayElement(), fixedSource, "display", focus, pattern); - checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), fixedSource, "userSelected", focus, pattern); - } - - private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) { - String code = element.getNamedChildValue("code"); - String system = element.getNamedChildValue("system"); - String display = element.getNamedChildValue("display"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); - - 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, checkDisplay, stack)) - 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")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - Coding c = ObjectConverter.readAsCoding(element); - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); - else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); - 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"), c, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set. " + getErrorMessage(vr.getMessage())); - 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"), c, stack); - else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code. " + getErrorMessage(vr.getMessage())); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set. " + getErrorMessage(vr.getMessage())); - } - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); - } - } - } - - private boolean isValueSet(String url) { - try { - ValueSet vs = context.fetchResourceWithException(ValueSet.class, url); - return vs != null; - } catch (Exception e) { - return false; - } - } - - private void checkContactPoint(List errors, String path, Element focus, ContactPoint fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); - - } - - private StructureDefinition checkExtension(ValidatorHostContext hostContext, List errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { - String url = element.getNamedChildValue("url"); - boolean isModifier = element.getName().equals("modifierExtension"); - - long t = System.nanoTime(); - StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null; - sdTime = sdTime + (System.nanoTime() - t); - if (ex == null) { - if (xverManager == null) { - xverManager = new XVerExtensionManager(context); - } - if (xverManager.matchingUrl(url)) { - switch (xverManager.status(url)) { - case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion '" + xverManager.getVersion(url) + "')"); - break; - case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id '" + xverManager.getElementId(url) + "')"); - break; - case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id '" + xverManager.getElementId(url) + "' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); - break; - case Valid: - ex = xverManager.makeDefinition(url); - context.generateSnapshot(ex); - context.cacheResource(ex); - break; - default: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' evaluation state illegal"); - break; - } - } else if (extensionUrl != null && !isAbsolute(url)) { - if (extensionUrl.equals(profile.getUrl())) { - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), "Sub-extension url '" + url + "' is not defined by the Extension " + profile.getUrl()); - } - } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), "The extension " + url + " is unknown, and not allowed here")) { - hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), "Unknown extension " + url); - } - } - if (ex != null) { - trackUsage(ex, hostContext, element); - if (def.getIsModifier()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not"); - } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is"); - } - // two questions - // 1. can this extension be used here? - checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); - - if (isModifier) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must be used as a modifierExtension"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must not be used as an extension (it's a modifierExtension)"); - - // check the type of the extension: - Set allowedTypes = listExtensionTypes(ex); - String actualType = getExtensionType(element); - if (actualType == null) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, 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, true, url); - - } - return ex; - } - - private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { - for (ElementDefinition ed : profile.getSnapshot().getElement()) { - if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { - return true; - } - } - return false; - } - - private String getExtensionType(Element element) { - for (Element e : element.getChildren()) { - if (e.getName().startsWith("value")) { - String tn = e.getName().substring(5); - String ltn = Utilities.uncapitalize(tn); - if (isPrimitiveType(ltn)) - return ltn; - else - return tn; - } - } + public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (externalHostServices != null) + return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); + else return null; } - private Set listExtensionTypes(StructureDefinition ex) { - ElementDefinition vd = null; - for (ElementDefinition ed : ex.getSnapshot().getElement()) { - if (ed.getPath().startsWith("Extension.value")) { - vd = ed; - break; - } - } - Set res = new HashSet(); - if (vd != null && !"0".equals(vd.getMax())) { - for (TypeRefComponent tr : vd.getType()) { - res.add(tr.getWorkingCode()); - } - } + @Override + public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (externalHostServices != null) + return externalHostServices.resolveConstantType(c.getAppContext(), name); + else + return null; + } + + @Override + public boolean log(String argument, List focus) { + if (externalHostServices != null) + return externalHostServices.log(argument, focus); + else + return false; + } + + @Override + public FunctionDetails resolveFunction(String functionName) { + throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); + } + + @Override + public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); + } + + @Override + public List executeFunction(Object appContext, String functionName, List> parameters) { + throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); + } + + @Override + public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + + if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { + return (Base) refContext.getUserData("validator.bundle.resolution"); + } + + if (c.getAppContext() instanceof Element) { + Element bnd = (Element) c.getAppContext(); + Base res = resolveInBundle(url, bnd); + if (res != null) + return res; + } + Base res = resolveInBundle(url, c.getResource()); + if (res != null) return res; + res = resolveInBundle(url, c.getContainer()); + if (res != null) + return res; + + if (externalHostServices != null) + return externalHostServices.resolveReference(c.getAppContext(), url, refContext); + else if (fetcher != null) + try { + return fetcher.fetch(c.getAppContext(), url); + } catch (IOException e) { + throw new FHIRException(e); + } + else + throw new Error("Not done yet - resolve " + url + " locally (2)"); + } - private boolean checkExtensionContext(List errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext) { - String extUrl = definition.getUrl(); - boolean ok = false; - CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder(); - List plist = new ArrayList<>(); - plist.add(stripIndexes(stack.getLiteralPath())); - for (String s : stack.getLogicalPaths()) { - String p = stripIndexes(s); - // all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition.... - if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) { - return true; - } - plist.add(p); - + public Base resolveInBundle(String url, Element bnd) { + if (bnd == null) + return null; + if (bnd.fhirType().equals("Bundle")) { + for (Element be : bnd.getChildrenByName("entry")) { + Element res = be.getNamedChild("resource"); + if (res != null) { + String fullUrl = be.getChildValue("fullUrl"); + String rt = res.fhirType(); + String id = res.getChildValue("id"); + if (url.equals(fullUrl)) + return res; + if (url.equals(rt + "/" + id)) + return res; + } } + } + return null; + } - for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { - if (ok) { - break; - } - if (ctxt.getType() == ExtensionContextType.ELEMENT) { - String en = ctxt.getExpression(); - contexts.append("e:" + en); - if ("Element".equals(en)) { - ok = true; - } else if (en.equals("Resource") && container.isResource()) { - ok = true; - } - for (String p : plist) { - if (ok) { - break; - } - if (p.equals(en)) { - ok = true; - } else { - String pn = p; - String pt = ""; - if (p.contains(".")) { - pn = p.substring(0, p.indexOf(".")); - pt = p.substring(p.indexOf(".")); - } - StructureDefinition sd = context.fetchTypeDefinition(pn); - while (sd != null) { - if ((sd.getType() + pt).equals(en)) { - ok = true; - break; - } - if (sd.getBaseDefinition() != null) { - sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); - } else { - sd = null; - } - } - } - } - } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { - contexts.append("x:" + ctxt.getExpression()); - NodeStack estack = stack.parent; - if (estack != null && estack.getElement().fhirType().equals("Extension")) { - String ext = estack.element.getNamedChildValue("url"); - if (ctxt.getExpression().equals(ext)) { - ok = true; - } - } - } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { - contexts.append("p:" + ctxt.getExpression()); - // The context is all elements that match the FHIRPath query found in the expression. - List res = fpe.evaluate(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(ctxt.getExpression())); - if (res.contains(container)) { - ok = true; - } - } else { - throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); - } + @Override + public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + ValidatorHostContext ctxt = (ValidatorHostContext) appContext; + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + if (sd == null) { + throw new FHIRException("Unable to resolve " + url); + } + InstanceValidator self = InstanceValidator.this; + List valerrors = new ArrayList(); + if (item instanceof Resource) { + try { + Element e = new ObjectConverter(context).convert((Resource) item); + self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); + } catch (IOException e1) { + throw new FHIRException(e1); } - if (!ok) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, "The extension " + extUrl + " is not allowed to be used at this point (allowed = " + contexts.toString() + "; this element is [" + plist.toString() + ")"); - return false; + } else if (item instanceof Element) { + Element e = (Element) item; + if (e.isResource()) { + self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); } else { - if (definition.hasContextInvariant()) { - for (StringType s : definition.getContextInvariant()) { - if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, - "The extension " + extUrl + " is not allowed to be used at this point (based on context invariant '" + s.getValue() + "')"); - return false; - } - } - } - return true; + throw new FHIRException("Not supported yet"); } + } else + throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); + boolean ok = true; + List record = new ArrayList<>(); + for (ValidationMessage v : valerrors) { + ok = ok && !v.getLevel().isError(); + if (v.getLevel().isError() || v.isSlicingHint()) { + record.add(v); + } + } + if (!ok && !record.isEmpty()) { + ctxt.sliceNotes(url, record); + } + return ok; } - private List fixContexts(String extUrl, List list) { - List res = new ArrayList<>(); - for (StructureDefinitionContextComponent ctxt : list) { - res.add(ctxt.copy()); - } - if ("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type".equals(extUrl)) { - list.get(0).setExpression("ElementDefinition.type"); - } - if ("http://hl7.org/fhir/StructureDefinition/regex".equals(extUrl)) { - list.get(1).setExpression("ElementDefinition.type"); - } - return list; - } - - private String stripIndexes(String path) { - boolean skip = false; - StringBuilder b = new StringBuilder(); - for (char c : path.toCharArray()) { - if (skip) { - if (c == ']') { - skip = false; - } - } else if (c == '[') { - skip = true; - } else { - b.append(c); - } - } - return b.toString(); - } - - private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent) { - checkFixedValue(errors, path, focus, fixed, fixedSource, propName, parent, false); - } - - @SuppressWarnings("rawtypes") - private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) { - if ((fixed == null || fixed.isEmpty()) && focus == null) { - ; // this is all good - } else if ((fixed == null || fixed.isEmpty()) && focus != null) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern, "The element " + focus.getName() + " is present in the instance but not allowed in the applicable " + (pattern ? "pattern" : "fixed value") + " specified in profile"); - } else if (fixed != null && !fixed.isEmpty() && focus == null) { - rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false, "Missing element '" + propName + "' - required by fixed value assigned in profile " + fixedSource); - } else { - String value = focus.primitiveValue(); - if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.CodeType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.StringType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.StringType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.UriType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UriType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DateType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.OidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.OidType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UuidType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.IdType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IdType) fixed).getValue() + "'"); - else if (fixed instanceof Quantity) - checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); - else if (fixed instanceof Address) - checkAddress(errors, path, focus, (Address) fixed, fixedSource, pattern); - else if (fixed instanceof ContactPoint) - checkContactPoint(errors, path, focus, (ContactPoint) fixed, fixedSource, pattern); - else if (fixed instanceof Attachment) - checkAttachment(errors, path, focus, (Attachment) fixed, fixedSource, pattern); - else if (fixed instanceof Identifier) - checkIdentifier(errors, path, focus, (Identifier) fixed, fixedSource, pattern); - else if (fixed instanceof Coding) - checkCoding(errors, path, focus, (Coding) fixed, fixedSource, pattern); - else if (fixed instanceof HumanName) - checkHumanName(errors, path, focus, (HumanName) fixed, fixedSource, pattern); - else if (fixed instanceof CodeableConcept) - checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed, fixedSource, pattern); - else if (fixed instanceof Timing) - checkTiming(errors, path, focus, (Timing) fixed, fixedSource, pattern); - else if (fixed instanceof Period) - checkPeriod(errors, path, focus, (Period) fixed, fixedSource, pattern); - else if (fixed instanceof Range) - checkRange(errors, path, focus, (Range) fixed, fixedSource, pattern); - else if (fixed instanceof Ratio) - checkRatio(errors, path, focus, (Ratio) fixed, fixedSource, pattern); - else if (fixed instanceof SampledData) - checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); - + @Override + public ValueSet resolveValueSet(Object appContext, String url) { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (c.getProfile() != null && url.startsWith("#")) { + for (Resource r : c.getProfile().getContained()) { + if (r.getId().equals(url.substring(1))) { + if (r instanceof ValueSet) + return (ValueSet) r; else - rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false, "Unhandled fixed value type " + fixed.getClass().getName()); - List extensions = new ArrayList(); - focus.getNamedChildren("extension", extensions); - if (fixed.getExtension().size() == 0) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0, "No extensions allowed, as the specified fixed value doesn't contain any extensions"); - } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(), - "Extensions count mismatch: expected " + Integer.toString(fixed.getExtension().size()) + " but found " + Integer.toString(extensions.size()))) { - for (Extension e : fixed.getExtension()) { - Element ex = getExtensionByUrl(extensions, e.getUrl()); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, "Extension count mismatch: unable to find extension: " + e.getUrl())) { - checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); - } - } - } + throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); + } } + return null; + } + return context.fetchResource(ValueSet.class, url); } - private void checkHumanName(List errors, String path, Element focus, HumanName fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + } - List parts = new ArrayList(); - focus.getNamedChildren("family", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), - "Expected " + (fixed.hasFamily() ? "1" : "0") + " but found " + Integer.toString(parts.size()) + " family elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); - } - focus.getNamedChildren("given", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), - "Expected " + Integer.toString(fixed.getGiven().size()) + " but found " + Integer.toString(parts.size()) + " given elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); - } - focus.getNamedChildren("prefix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), - "Expected " + Integer.toString(fixed.getPrefix().size()) + " but found " + Integer.toString(parts.size()) + " prefix elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); - } - focus.getNamedChildren("suffix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), - "Expected " + Integer.toString(fixed.getSuffix().size()) + " but found " + Integer.toString(parts.size()) + " suffix elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); - } + private IWorkerContext context; + private FHIRPathEngine fpe; + + // configuration items + private CheckDisplayOption checkDisplay; + private boolean anyExtensionsAllowed; + private boolean errorForUnknownProfiles; + private boolean noInvariantChecks; + private boolean noTerminologyChecks; + private boolean hintAboutNonMustSupport; + private boolean showMessagesFromReferences; + private BestPracticeWarningLevel bpWarnings; + private String validationLanguage; + private boolean baseOnly; + + private List extensionDomains = new ArrayList(); + + private IdStatus resourceIdRule; + private boolean allowXsiLocation; + + // used during the build process to keep the overall volume of messages down + private boolean suppressLoincSnomedMessages; + + // time tracking + private long overall = 0; + private long txTime = 0; + private long sdTime = 0; + private long loadTime = 0; + private long fpeTime = 0; + + private boolean noBindingMsgSuppressed; + private boolean debug; + private Map fetchCache = new HashMap<>(); + private HashMap resourceTracker = new HashMap<>(); + private IValidatorResourceFetcher fetcher; + long time = 0; + private IEvaluationContext externalHostServices; + private boolean noExtensibleWarnings; + private String serverBase; + + private EnableWhenEvaluator myEnableWhenEvaluator = new EnableWhenEvaluator(); + private String executionId; + private XVerExtensionManager xverManager; + private IValidationProfileUsageTracker tracker; + private ValidatorHostServices validatorServices; + private boolean assumeValidRestReferences; + private boolean allowExamples; + private ResourceBundle messages = + ResourceBundle.getBundle("Messages", Locale.US); + + public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { + super(); + this.context = theContext; + this.externalHostServices = hostServices; + fpe = new FHIRPathEngine(context); + validatorServices = new ValidatorHostServices(); + fpe.setHostServices(validatorServices); + if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0")) + fpe.setLegacyMode(true); + source = Source.InstanceValidator; + } + + @Override + public boolean isNoExtensibleWarnings() { + return noExtensibleWarnings; + } + + @Override + public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings) { + this.noExtensibleWarnings = noExtensibleWarnings; + return this; + } + + @Override + public boolean isShowMessagesFromReferences() { + return showMessagesFromReferences; + } + + @Override + public void setShowMessagesFromReferences(boolean showMessagesFromReferences) { + this.showMessagesFromReferences = showMessagesFromReferences; + } + + @Override + public boolean isNoInvariantChecks() { + return noInvariantChecks; + } + + @Override + public IResourceValidator setNoInvariantChecks(boolean value) { + this.noInvariantChecks = value; + return this; + } + + public IValidatorResourceFetcher getFetcher() { + return this.fetcher; + } + + public IResourceValidator setFetcher(IValidatorResourceFetcher value) { + this.fetcher = value; + return this; + } + + public IValidationProfileUsageTracker getTracker() { + return this.tracker; + } + + public IResourceValidator setTracker(IValidationProfileUsageTracker value) { + this.tracker = value; + return this; + } + + + public boolean isHintAboutNonMustSupport() { + return hintAboutNonMustSupport; + } + + public void setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) { + this.hintAboutNonMustSupport = hintAboutNonMustSupport; + } + + public boolean isAssumeValidRestReferences() { + return this.assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean value) { + this.assumeValidRestReferences = value; + } + + public boolean isAllowExamples() { + return this.allowExamples; + } + + public void setAllowExamples(boolean value) { + this.allowExamples = value; + } + + + private boolean allowUnknownExtension(String url) { + if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression")) + // Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with + return true; + for (String s : extensionDomains) + if (url.startsWith(s)) + return true; + return anyExtensionsAllowed; + } + + private boolean isKnownExtension(String url) { + // Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with + if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION)) + return true; + for (String s : extensionDomains) + if (url.startsWith(s)) + return true; + return false; + } + + private void bpCheck(List errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) { + if (bpWarnings != null) { + switch (bpWarnings) { + case Error: + rule(errors, invalid, line, col, literalPath, test, message); + break; + case Warning: + warning(errors, invalid, line, col, literalPath, test, message); + break; + case Hint: + hint(errors, invalid, line, col, literalPath, test, message); + break; + default: // do nothing + break; + } } + } - private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { - String system = element.getNamedChildValue("system"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Identifier.system must be an absolute reference, not a local reference"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format) throws FHIRException { + return validate(appContext, errors, stream, format, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); } + return validate(appContext, errors, stream, format, profiles); + } - private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".type", focus.getNamedChild("type"), fixed.getType(), fixedSource, "type", focus, pattern); - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); - checkFixedValue(errors, path + ".assigner", focus.getNamedChild("assigner"), fixed.getAssigner(), fixedSource, "assigner", focus, pattern); + private StructureDefinition getSpecifiedProfile(String profile) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); + if (sd == null) { + throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); } + return sd; + } - private void checkPeriod(List errors, String path, Element focus, Period fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".start", focus.getNamedChild("start"), fixed.getStartElement(), fixedSource, "start", focus, pattern); - checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), fixedSource, "end", focus, pattern); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, List profiles) throws FHIRException { + ParserBase parser = Manager.makeParser(context, format); + if (parser instanceof XmlParser) + ((XmlParser) parser).setAllowXsiLocation(allowXsiLocation); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(stream); + } catch (IOException e1) { + throw new FHIRException(e1); } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } - private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { - if (isBlank(e.primitiveValue())) { - if (e.primitiveValue() == null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions"); - else if (e.primitiveValue().length() == 0) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value that is not empty"); - else if (StringUtils.isWhitespace(e.primitiveValue())) - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types should not only be whitespace"); - return; - } - String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); - if (regex != null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), "Element value '" + e.primitiveValue() + "' does not meet regex '" + regex + "'"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource) throws FHIRException { + return validate(appContext, errors, resource, new ArrayList<>()); + } - if (type.equals("boolean")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'"); - } - if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { - String url = e.primitiveValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), "URI values cannot start with oid:"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), "URI values cannot start with uuid:"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) - // work around an old invalid example in a core package - || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), "URI values cannot have whitespace('" + url + "')"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - - if (type.equals("oid")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), "OIDs must start with urn:oid:")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), "OIDs must be valid"); - } - if (type.equals("uuid")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:"); - try { - UUID.fromString(url.substring(8)); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid (" + ex.getMessage() + ")"); - } - } - - // now, do we check the URI target? - if (fetcher != null) { - boolean found; - try { - found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url); - } catch (IOException e1) { - found = false; - } - rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '" + url + "' does not resolve"); - } - } - if (type.equals("id")) { - // work around an old issue with ElementDefinition.id - if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), "id value '" + e.primitiveValue() + "' is not valid"); - } - } - if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, "@value cannot be empty")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), "value should not start or finish with whitespace"); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, "value is longer than permitted maximum length of 1 MB (1048576 bytes)")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - } - } - } - if (type.equals("dateTime")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue() - .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), - "Not a valid date time"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), "if a date has a time, it must have a timezone"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - try { - DateTimeType dt = new DateTimeType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date/time (" + ex.getMessage() + ")"); - } - } - if (type.equals("time")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue() - .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"), - "Not a valid time"); - try { - TimeType dt = new TimeType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid time (" + ex.getMessage() + ")"); - } - } - if (type.equals("date")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), - "Not a valid date"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum value of " + context.getMaxLength()); - try { - DateType dt = new DateType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date (" + ex.getMessage() + ")"); - } - } - if (type.equals("base64Binary")) { - String encoded = e.primitiveValue(); - if (isNotBlank(encoded)) { - /* - * Technically this is not bulletproof as some invalid base64 won't be caught, - * but I think it's good enough. The original code used Java8 Base64 decoder - * but I've replaced it with a regex for 2 reasons: - * 1. This code will run on any version of Java - * 2. This code doesn't actually decode, which is much easier on memory use for big payloads - */ - int charCount = 0; - for (int i = 0; i < encoded.length(); i++) { - char nextChar = encoded.charAt(i); - if (Character.isWhitespace(nextChar)) { - continue; - } - if (Character.isLetterOrDigit(nextChar)) { - charCount++; - } - if (nextChar == '/' || nextChar == '=' || nextChar == '+') { - charCount++; - } - } - - if (charCount > 0 && charCount % 4 != 0) { - String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value '{0}' is not a valid Base64 value", value); - } - } - } - if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer")) { - Integer v = new Integer(e.getValue()).intValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); - if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); - if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); - } - } - if (type.equals("integer64")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer64")) { - Long v = new Long(e.getValue()).longValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); - if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); - if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); - } - } - if (type.equals("decimal")) { - if (e.primitiveValue() != null) { - DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is not a valid decimal")) - warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is outside the range of commonly/reasonably supported decimals"); - } - } - if (type.equals("instant")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), - "The instant '" + e.primitiveValue() + "' is not valid (by regex)"); - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - try { - InstantType dt = new InstantType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid instant (" + ex.getMessage() + ")"); - } - } - - if (type.equals("code") && e.primitiveValue() != null) { - // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace - // other than single spaces in the contents - rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()), "The code '" + e.primitiveValue() + "' is not valid (whitespace rules)"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - } - - if (context.hasBinding() && e.primitiveValue() != null) { - checkPrimitiveBinding(errors, path, type, context, e, profile, node); - } - - if (type.equals("xhtml")) { - XhtmlNode xhtml = e.getXhtml(); - if (xhtml != null) { // if it is null, this is an error already noted in the parsers - // check that the namespace is there and correct. - String ns = xhtml.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); - // check that inner namespaces are all correct - checkInnerNS(errors, e, path, xhtml.getChildNodes()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), "Wrong name on the XHTML ('" + ns + "') - must start with div"); - // check that no illegal elements and attributes have been used - checkInnerNames(errors, e, path, xhtml.getChildNodes()); - } - } - - if (context.hasFixed()) { - checkFixedValue(errors, path, e, context.getFixed(), profile.getUrl(), context.getSliceName(), null, false); - } - if (context.hasPattern()) { - checkFixedValue(errors, path, e, context.getPattern(), profile.getUrl(), context.getSliceName(), null, true); - } - - // for nothing to check + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); } + return validate(appContext, errors, resource, profiles); + } - private boolean isDefinitionURL(String url) { - return Utilities.existsInList(url, "http://hl7.org/fhirpath/System.Boolean", "http://hl7.org/fhirpath/System.String", "http://hl7.org/fhirpath/System.Integer", - "http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, List profiles) throws FHIRException { + long t = System.nanoTime(); + Element e; + try { + e = new ObjectConverter(context).convert(resource); + } catch (IOException e1) { + throw new FHIRException(e1); } + loadTime = System.nanoTime() - t; + validate(appContext, errors, e, profiles); + return e; + } - private void checkInnerNames(List errors, Element e, String path, List list) { - for (XhtmlNode node : list) { - if (node.getNodeType() == NodeType.Element) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), - "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", - "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", - "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", - "code", "samp", "img", "map", "area" + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element) throws FHIRException { + return validate(appContext, errors, element, new ArrayList<>()); + } - ), "Illegal element name in the XHTML ('" + node.getName() + "')"); - for (String an : node.getAttributes().keySet()) { - boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, - "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", - // tables - "span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") || + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, element, profiles); + } - Utilities.existsInList(node.getName() + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite", - "a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src", - "img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape", - "area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border", - "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" - ); - if (!ok) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Illegal attribute name in the XHTML ('" + an + "' on '" + node.getName() + "')"); - } - checkInnerNames(errors, e, path, node.getChildNodes()); - } + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, List profiles) throws FHIRException { + XmlParser parser = new XmlParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(element); + } catch (IOException e1) { + throw new FHIRException(e1); + } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document) throws FHIRException { + return validate(appContext, errors, document, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, document, profiles); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, List profiles) throws FHIRException { + XmlParser parser = new XmlParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(document); + } catch (IOException e1) { + throw new FHIRException(e1); + } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object) throws FHIRException { + return validate(appContext, errors, object, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, object, profiles); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, List profiles) throws FHIRException { + JsonParser parser = new JsonParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e = parser.parse(object); + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public void validate(Object appContext, List errors, Element element) throws FHIRException { + validate(appContext, errors, element, new ArrayList<>()); + } + + @Override + public void validate(Object appContext, List errors, Element element, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + validate(appContext, errors, element, profiles); + } + + @Override + public void validate(Object appContext, List errors, Element element, List profiles) throws FHIRException { + // this is the main entry point; all the other public entry points end up here coming here... + // so the first thing to do is to clear the internal state + fetchCache.clear(); + fetchCache.put(element.fhirType() + "/" + element.getIdBase(), element); + resourceTracker.clear(); + executionId = UUID.randomUUID().toString(); + baseOnly = profiles.isEmpty(); + + long t = System.nanoTime(); + if (profiles == null || profiles.isEmpty()) { + validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(element)); + } else { + for (StructureDefinition defn : profiles) { + validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(element)); + } + } + if (hintAboutNonMustSupport) { + checkElementUsage(errors, element, new NodeStack(element)); + } + overall = System.nanoTime() - t; + } + + private void checkElementUsage(List errors, Element element, NodeStack stack) { + String elementUsage = element.getUserString("elementSupported"); + hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"),messages.getString("The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile"), element.getName(), element.getProperty().getStructure().getUrl()); + + if (element.hasChildren()) { + String prevName = ""; + int elementCount = 0; + for (Element ce : element.getChildren()) { + if (ce.getName().equals(prevName)) + elementCount++; + else { + elementCount = 1; + prevName = ce.getName(); } + checkElementUsage(errors, ce, stack.push(ce, elementCount, null, null)); + } } + } - private void checkInnerNS(List errors, Element e, String path, List list) { - for (XhtmlNode node : list) { - if (node.getNodeType() == NodeType.Element) { - String ns = node.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); - checkInnerNS(errors, e, path, node.getChildNodes()); - } + private boolean check(String v1, String v2) { + return v1 == null ? Utilities.noString(v1) : v1.equals(v2); + } + + private void checkAddress(List errors, String path, Element focus, Address fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + checkFixedValue(errors, path + ".city", focus.getNamedChild("city"), fixed.getCityElement(), fixedSource, "city", focus, pattern); + checkFixedValue(errors, path + ".state", focus.getNamedChild("state"), fixed.getStateElement(), fixedSource, "state", focus, pattern); + checkFixedValue(errors, path + ".country", focus.getNamedChild("country"), fixed.getCountryElement(), fixedSource, "country", focus, pattern); + checkFixedValue(errors, path + ".zip", focus.getNamedChild("zip"), fixed.getPostalCodeElement(), fixedSource, "postalCode", focus, pattern); + + List lines = new ArrayList(); + focus.getNamedChildren("line", lines); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(),messages.getString("Expected__but_found__line_elements"), Integer.toString(fixed.getLine().size()), Integer.toString(lines.size()))) { + for (int i = 0; i < lines.size(); i++) + checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); + } + } + + private void checkAttachment(List errors, String path, Element focus, Attachment fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".contentType", focus.getNamedChild("contentType"), fixed.getContentTypeElement(), fixedSource, "contentType", focus, pattern); + checkFixedValue(errors, path + ".language", focus.getNamedChild("language"), fixed.getLanguageElement(), fixedSource, "language", focus, pattern); + checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); + checkFixedValue(errors, path + ".url", focus.getNamedChild("url"), fixed.getUrlElement(), fixedSource, "url", focus, pattern); + checkFixedValue(errors, path + ".size", focus.getNamedChild("size"), fixed.getSizeElement(), fixedSource, "size", focus, pattern); + checkFixedValue(errors, path + ".hash", focus.getNamedChild("hash"), fixed.getHashElement(), fixedSource, "hash", focus, pattern); + checkFixedValue(errors, path + ".title", focus.getNamedChild("title"), fixed.getTitleElement(), fixedSource, "title", focus, pattern); + } + + // public API + private boolean checkCode(List errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) 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(new ValidationOptions(stack.workingLang), system, code, checkDisplay ? display : null); + txTime = txTime + (System.nanoTime() - t); + if (s == null) + return true; + if (s.isOk()) { + if (s.getMessage() != null) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + return true; + } + if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else if (s.getSeverity() == IssueSeverity.INFORMATION) + txHint(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else if (s.getSeverity() == IssueSeverity.WARNING) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else + return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage() + " for '" + system + "#" + code + "'"); + return true; + } else if (system.startsWith("http://hl7.org/fhir")) { + if (Utilities.existsInList(system, "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/srt")) + return true; // else don't check these (for now) + else if (system.startsWith("http://hl7.org/fhir/test")) + return true; // we don't validate these + else { + CodeSystem cs = getCodeSystem(system); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null,messages.getString("Unknown_Code_System_"), system)) { + ConceptDefinitionComponent def = getCodeDefinition(cs, code); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null,messages.getString("Unknown_Code_"), system, code)) + return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()),messages.getString("Display_should_be_"), def.getDisplay()); } - } - - private void checkPrimitiveBinding(List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { - // We ignore bindings that aren't on string, uri or code - if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { - return; + return false; + } + } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { + return true; // no checks in this case + } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI_"), system); + return false; + } else { + try { + if (context.fetchResourceWithException(ValueSet.class, system) != null) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system"), system); + // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. } - if (noTerminologyChecks) - return; - - String value = element.primitiveValue(); - // System.out.println("check "+value+" in "+path); - - // firstly, resolve the value set - ElementDefinitionBindingComponent binding = elementContext.getBinding(); - if (binding.hasValueSet()) { - ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, "ValueSet {0} not found by validator", describeReference(binding.getValueSet()))) { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), value, vs); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') could not be validated in the absence of a terminology server"); - else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)" + getErrorMessage(vr.getMessage())); - 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) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)" + getErrorMessage(vr.getMessage())); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is recommended to come from this value set)" + getErrorMessage(vr.getMessage())); - } - } - } - } - } else if (!noBindingMsgSuppressed) - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), "Binding has no source, so can't be checked"); + hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false,messages.getString("Code_System_URI__is_unknown_so_the_code_cannot_be_validated"), system); + return true; + } catch (Exception e) { + return true; + } } + } - private void checkQuantity(List 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); - checkFixedValue(errors, path + ".units", focus.getNamedChild("unit"), fixed.getUnitElement(), fixedSource, "units", focus, pattern); - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); - } + private boolean startsWithButIsNot(String system, String... uri) { + for (String s : uri) + if (!system.equals(s) && system.startsWith(s)) + return true; + return false; + } - // implementation - private void checkRange(List errors, String path, Element focus, Range fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".low", focus.getNamedChild("low"), fixed.getLow(), fixedSource, "low", focus, pattern); - checkFixedValue(errors, path + ".high", focus.getNamedChild("high"), fixed.getHigh(), fixedSource, "high", focus, pattern); - - } - - private void checkRatio(List errors, String path, Element focus, Ratio fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".numerator", focus.getNamedChild("numerator"), fixed.getNumerator(), fixedSource, "numerator", focus, pattern); - checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), fixedSource, "denominator", focus, pattern); - } - - private void checkReference(ValidatorHostContext hostContext, List errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException { - Reference reference = ObjectConverter.readAsReference(element); - - String ref = reference.getReference(); - if (Utilities.noString(ref)) { - if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display"); - } - return; - } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { - // special known URLs that can't be validated but are known to be valid - return; + 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; + } - ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); - String refType; - if (ref.startsWith("#")) { - refType = "contained"; - } else { - if (we == null) { - refType = "remote"; + private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + List codings = new ArrayList(); + focus.getNamedChildren("coding", codings); + if (pattern) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() >= fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + for (int i = 0; i < fixed.getCoding().size(); i++) { + Coding fixedCoding = fixed.getCoding().get(i); + boolean found = false; + List allErrorsFixed = new ArrayList<>(); + List errorsFixed; + for (int j = 0; j < codings.size() && !found; ++j) { + errorsFixed = new ArrayList<>(); + checkFixedValue(errorsFixed, path + ".coding", codings.get(j), fixedCoding, fixedSource, "coding", focus, pattern); + if (!hasErrors(errorsFixed)) { + found = true; } else { - refType = "bundled"; + errorsFixed + .stream() + .filter(t -> t.getLevel().ordinal() >= IssueSeverity.ERROR.ordinal()) + .forEach(t -> allErrorsFixed.add(t)); } + } + if (!found) { + // The argonaut DSTU2 labs profile requires userSelected=false on the category.coding and this + // needs to produce an understandable error message + String message = "Expected CodeableConcept " + (pattern ? "pattern" : "fixed value") + " not found for" + + " system: " + fixedCoding.getSystemElement().asStringValue() + + " code: " + fixedCoding.getCodeElement().asStringValue() + + " display: " + fixedCoding.getDisplayElement().asStringValue(); + if (fixedCoding.hasUserSelected()) { + message += " userSelected: " + fixedCoding.getUserSelected(); + } + message += " - Issues: " + allErrorsFixed; + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, false, message); + } } - ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundled") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.getAppContext(), path, ref); + } + } else { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + for (int i = 0; i < codings.size(); i++) + checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), fixedSource, "coding", focus); + } + } + } - if (pol.checkExists()) { - if (we == null) { - if (fetcher == null) { - if (!refType.equals("contained")) - throw new FHIRException("Resource resolution services not provided"); - } else { - Element ext = null; - if (fetchCache.containsKey(ref)) { - ext = fetchCache.get(ref); - } else { - try { - ext = fetcher.fetch(hostContext.getAppContext(), ref); - } catch (IOException e) { - throw new FHIRException(e); - } - if (ext != null) { - fetchCache.put(ref, ext); - } - } - we = ext == null ? null : makeExternalRef(ext, path); + private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) { + 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,messages.getString("Binding_for__missing_cc"), path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + try { + CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); + if (!cc.hasCoding()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + else + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), "Unable to resolve resource '" + ref + "'"); - } + } else { + long t = System.nanoTime(); - String ft; - if (we != null) - ft = we.getType(); + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + 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"), cc, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } + } + } else { + if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + 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"), cc, stack); + if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } + } + } + } else if (vr.getMessage() != null) { + res = false; + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + res = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).noCheckValueSetMembership(), nextCoding, valueset); + if (vr.getSeverity() != null) { + if (vr.getSeverity() == IssueSeverity.INFORMATION) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else if (vr.getSeverity() == IssueSeverity.WARNING) { + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } + } + } + } + } + txTime = txTime + (System.nanoTime() - t); + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + } else if (!noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + } + } + } + return res; + } + + private boolean checkTerminologyCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { + 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,messages.getString("Binding_for__missing_cc"), path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + try { + CodeableConcept cc = convertToCodeableConcept(element, logical); + if (!cc.hasCoding()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_is_required_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + else + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); + } + } else { + long t = System.nanoTime(); + + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + 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"), cc, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } + } + } else { + if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + 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"), cc, stack); + if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } + } + } + } else if (vr.getMessage() != null) { + res = false; + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + res = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + String nextCode = nextCoding.getCode(); + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); + if (!vr.isOk()) { + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Code_0_is_not_a_valid_code_in_code_system_1"), nextCode, nextSystem); + } + } + } + } + txTime = txTime + (System.nanoTime() - t); + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); + } + // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. + if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { + checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + } else if (!noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + } + } + } + return res; + } + + private void checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { + Coding c = convertToCoding(element, logical); + String code = c.getCode(); + String system = c.getSystem(); + String display = c.getDisplay(); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); + + if (system != null && code != null && !noTerminologyChecks) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); + try { + if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) + if (theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + try { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); + 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"), c, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); + } + } + } else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + 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"), c, stack); + else + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + } + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + } + } + } + } catch (Exception e) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); + } + } + } + + private CodeableConcept convertToCodeableConcept(Element element, StructureDefinition logical) { + CodeableConcept res = new CodeableConcept(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.getCodingFirstRep().setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.getCodingFirstRep().setSystem(url); + } else { + res.getCodingFirstRep().setSystem("urn:oid:" + oid); + } + } else if ("Coding.version".equals(m)) { + res.getCodingFirstRep().setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.getCodingFirstRep().setDisplay(list.get(0).primitiveValue()); + } else if ("CodeableConcept.text".equals(m)) { + res.setText(list.get(0).primitiveValue()); + } else if ("CodeableConcept.coding".equals(m)) { + StructureDefinition c = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode()); + for (Element e : list) { + res.addCoding(convertToCoding(e, c)); + } + } + } + } + } + } + return res; + } + + private Coding convertToCoding(Element element, StructureDefinition logical) { + Coding res = new Coding(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.setSystem(url); + } else { + res.setSystem("urn:oid:" + oid); + } + } else if ("Coding.version".equals(m)) { + res.setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.setDisplay(list.get(0).primitiveValue()); + } + } + } + } + } + return res; + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + } + } + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (code = " + c.getSystem() + "#" + c.getCode() + ")"); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + } + } + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + } + } + } + + private String ccSummary(CodeableConcept cc) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (Coding c : cc.getCoding()) + b.append(c.getSystem() + "#" + c.getCode()); + return b.toString(); + } + + private void checkCoding(List errors, String path, Element focus, Coding fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".version", focus.getNamedChild("version"), fixed.getVersionElement(), fixedSource, "version", focus, pattern); + checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); + checkFixedValue(errors, path + ".display", focus.getNamedChild("display"), fixed.getDisplayElement(), fixedSource, "display", focus, pattern); + checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), fixedSource, "userSelected", focus, pattern); + } + + private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) { + String code = element.getNamedChildValue("code"); + String system = element.getNamedChildValue("system"); + String display = element.getNamedChildValue("display"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); + + if (system != null && code != null && !noTerminologyChecks) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); + try { + if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) + if (theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + try { + Coding c = ObjectConverter.readAsCoding(element); + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); + 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"), c, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); + } + } + } else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set. " + getErrorMessage(vr.getMessage())); + 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"), c, stack); + else + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + } + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + } + } + } + } catch (Exception e) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); + } + } + } + + private boolean isValueSet(String url) { + try { + ValueSet vs = context.fetchResourceWithException(ValueSet.class, url); + return vs != null; + } catch (Exception e) { + return false; + } + } + + private void checkContactPoint(List errors, String path, Element focus, ContactPoint fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + + } + + private StructureDefinition checkExtension(ValidatorHostContext hostContext, List errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { + String url = element.getNamedChildValue("url"); + boolean isModifier = element.getName().equals("modifierExtension"); + + long t = System.nanoTime(); + StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null; + sdTime = sdTime + (System.nanoTime() - t); + if (ex == null) { + if (xverManager == null) { + xverManager = new XVerExtensionManager(context); + } + if (xverManager.matchingUrl(url)) { + switch (xverManager.status(url)) { + case BadVersion: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_invalidVersion_"), url, xverManager.getVersion(url)); + break; + case Unknown: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_unknown_Element_id_"), url, xverManager.getElementId(url)); + break; + case Invalid: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_Element_id__is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions"), url, xverManager.getElementId(url)); + break; + case Valid: + ex = xverManager.makeDefinition(url); + context.generateSnapshot(ex); + context.cacheResource(ex); + break; + default: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__evaluation_state_illegal"), url); + break; + } + } else if (extensionUrl != null && !isAbsolute(url)) { + if (extensionUrl.equals(profile.getUrl())) { + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url),messages.getString("Subextension_url__is_not_defined_by_the_Extension_"), url, profile.getUrl()); + } + } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url),messages.getString("The_extension__is_unknown_and_not_allowed_here"), url)) { + hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url),messages.getString("Unknown_extension_"), url); + } + } + if (ex != null) { + trackUsage(ex, hostContext, element); + if (def.getIsModifier()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not")); + } else { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is")); + } + // two questions + // 1. can this extension be used here? + checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); + + if (isModifier) + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_be_used_as_a_modifierExtension"), url); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension"), url); + + // check the type of the extension: + Set allowedTypes = listExtensionTypes(ex); + String actualType = getExtensionType(element); + if (actualType == null) + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(),messages.getString("The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions"), url); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType),messages.getString("The_Extension__definition_allows_for_the_types__but_found_type_"), url, allowedTypes.toString(), 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, true, url); + + } + return ex; + } + + private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { + for (ElementDefinition ed : profile.getSnapshot().getElement()) { + if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { + return true; + } + } + return false; + } + + private String getExtensionType(Element element) { + for (Element e : element.getChildren()) { + if (e.getName().startsWith("value")) { + String tn = e.getName().substring(5); + String ltn = Utilities.uncapitalize(tn); + if (isPrimitiveType(ltn)) + return ltn; else - ft = tryParse(ref); + return tn; + } + } + return null; + } - if (reference.hasType()) { // R4 onwards... - // the type has to match the specified - String tu = isAbsolute(reference.getType()) ? reference.getType() : "http://hl7.org/fhir/StructureDefinition/" + reference.getType(); - TypeRefComponent containerType = container.getType("Reference"); - if (!containerType.hasTargetProfile(tu) && !containerType.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { - boolean matchingResource = false; - for (CanonicalType target : containerType.getTargetProfile()) { - StructureDefinition sd = resolveProfile(profile, target.asStringValue()); - if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) { - matchingResource = true; - break; - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, - "The type '" + reference.getType() + "' is not a valid Target for this element (must be one of " + container.getType("Reference").getTargetProfile() + ")"); + private Set listExtensionTypes(StructureDefinition ex) { + ElementDefinition vd = null; + for (ElementDefinition ed : ex.getSnapshot().getElement()) { + if (ed.getPath().startsWith("Extension.value")) { + vd = ed; + break; + } + } + Set res = new HashSet(); + if (vd != null && !"0".equals(vd.getMax())) { + for (TypeRefComponent tr : vd.getType()) { + res.add(tr.getWorkingCode()); + } + } + return res; + } - } - // the type has to match the actual - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), "The specified type '" + reference.getType() + "' does not match the found type '" + ft + "'"); - } + private boolean checkExtensionContext(List errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext) { + String extUrl = definition.getUrl(); + boolean ok = false; + CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder(); + List plist = new ArrayList<>(); + plist.add(stripIndexes(stack.getLiteralPath())); + for (String s : stack.getLogicalPaths()) { + String p = stripIndexes(s); + // all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition.... + if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) { + return true; + } + plist.add(p); - if (we != null && pol.checkType()) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, "Unable to determine type of target resource")) { - // we validate as much as we can. First, can we infer a type from the profile? - boolean ok = false; - TypeRefComponent type = getReferenceTypeRef(container.getType()); - if (type.hasTargetProfile() && !type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { - Set types = new HashSet<>(); - List profiles = new ArrayList<>(); - for (UriType u : type.getTargetProfile()) { - StructureDefinition sd = resolveProfile(profile, u.getValue()); - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, "Unable to resolve the profile reference '" + u.getValue() + "'")) { - types.add(sd.getType()); - if (ft.equals(sd.getType())) { - ok = true; - profiles.add(sd); - } - } - } - if (!pol.checkValid()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, "Unable to find matching profile for " + ref + " (by type) among choices: " + StringUtils.join("; ", type.getTargetProfile())); - } else { - Map> badProfiles = new HashMap>(); - Map> goodProfiles = new HashMap>(); - int goodCount = 0; - for (StructureDefinition pr : profiles) { - List profileErrors = new ArrayList(); - validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack()); - if (!hasErrors(profileErrors)) { - goodCount++; - goodProfiles.put(pr, profileErrors); - trackUsage(pr, hostContext, element); - } else { - badProfiles.put(pr, profileErrors); - } - } - if (goodCount == 1) { - if (showMessagesFromReferences) { - for (ValidationMessage vm : goodProfiles.values().iterator().next()) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - - } else if (goodProfiles.size() == 0) { - if (!isShowMessagesFromReferences()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); - for (StructureDefinition sd : badProfiles.keySet()) { - slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); - } - } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); - for (List messages : badProfiles.values()) { - for (ValidationMessage vm : messages) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - } - } else { - if (!isShowMessagesFromReferences()) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); - for (StructureDefinition sd : badProfiles.keySet()) { - slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); - } - } else { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); - for (List messages : goodProfiles.values()) { - for (ValidationMessage vm : messages) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - } - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + types.toString() + ")"); - } - if (type.hasAggregation()) { - boolean modeOk = false; - for (Enumeration mode : type.getAggregation()) { - if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained")) - modeOk = true; - else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled")) - modeOk = true; - else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) - modeOk = true; - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference"); - } - } - } - if (we == null) { - TypeRefComponent type = getReferenceTypeRef(container.getType()); - boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); - rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, "Bundled or contained reference not found within the bundle/resource " + ref); - } - if (we == null && ft != null && assumeValidRestReferences) { - // if we == null, we inferred ft from the reference. if we are told to treat this as gospel - TypeRefComponent type = getReferenceTypeRef(container.getType()); - Set types = new HashSet<>(); - for (CanonicalType tp : type.getTargetProfile()) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue()); - if (sd != null) { - types.add(sd.getType()); - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), "The type '" + ft + "' implied by the reference URL " + ref + " is not a valid Target for this element (must be one of " + types + ")"); - - } - if (pol == ReferenceValidationPolicy.CHECK_VALID) { - // todo.... - } } - private String asListByUrl(Collection list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (StructureDefinition sd : list) { - b.append(sd.getUrl()); + for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { + if (ok) { + break; + } + if (ctxt.getType() == ExtensionContextType.ELEMENT) { + String en = ctxt.getExpression(); + contexts.append("e:" + en); + if ("Element".equals(en)) { + ok = true; + } else if (en.equals("Resource") && container.isResource()) { + ok = true; } - return b.toString(); - } - - private String asList(Collection list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (CanonicalType c : list) { - b.append(c.getValue()); - } - return b.toString(); - } - - private boolean areAllBaseProfiles(List profiles) { - for (StructureDefinition sd : profiles) { - if (!sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { - return false; + for (String p : plist) { + if (ok) { + break; + } + if (p.equals(en)) { + ok = true; + } else { + String pn = p; + String pt = ""; + if (p.contains(".")) { + pn = p.substring(0, p.indexOf(".")); + pt = p.substring(p.indexOf(".")); } + StructureDefinition sd = context.fetchTypeDefinition(pn); + while (sd != null) { + if ((sd.getType() + pt).equals(en)) { + ok = true; + break; + } + if (sd.getBaseDefinition() != null) { + sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); + } else { + sd = null; + } + } + } } + } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { + contexts.append("x:" + ctxt.getExpression()); + NodeStack estack = stack.parent; + if (estack != null && estack.getElement().fhirType().equals("Extension")) { + String ext = estack.element.getNamedChildValue("url"); + if (ctxt.getExpression().equals(ext)) { + ok = true; + } + } + } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { + contexts.append("p:" + ctxt.getExpression()); + // The context is all elements that match the FHIRPath query found in the expression. + List res = fpe.evaluate(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(ctxt.getExpression())); + if (res.contains(container)) { + ok = true; + } + } else { + throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); + } + } + if (!ok) { + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_"), extUrl, contexts.toString(), plist.toString()); + return false; + } else { + if (definition.hasContextInvariant()) { + for (StringType s : definition.getContextInvariant()) { + if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_"), extUrl, s.getValue()); + return false; + } + } + } + return true; + } + } + + private List fixContexts(String extUrl, List list) { + List res = new ArrayList<>(); + for (StructureDefinitionContextComponent ctxt : list) { + res.add(ctxt.copy()); + } + if ("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type".equals(extUrl)) { + list.get(0).setExpression("ElementDefinition.type"); + } + if ("http://hl7.org/fhir/StructureDefinition/regex".equals(extUrl)) { + list.get(1).setExpression("ElementDefinition.type"); + } + return list; + } + + private String stripIndexes(String path) { + boolean skip = false; + StringBuilder b = new StringBuilder(); + for (char c : path.toCharArray()) { + if (skip) { + if (c == ']') { + skip = false; + } + } else if (c == '[') { + skip = true; + } else { + b.append(c); + } + } + return b.toString(); + } + + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent) { + checkFixedValue(errors, path, focus, fixed, fixedSource, propName, parent, false); + } + + @SuppressWarnings("rawtypes") + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) { + if ((fixed == null || fixed.isEmpty()) && focus == null) { + ; // this is all good + } else if ((fixed == null || fixed.isEmpty()) && focus != null) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern,messages.getString("The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile"), focus.getName(), (pattern ? "pattern" : "fixed value")); + } else if (fixed != null && !fixed.isEmpty() && focus == null) { + rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false,messages.getString("Missing_element___required_by_fixed_value_assigned_in_profile_"), propName, fixedSource); + } else { + String value = focus.primitiveValue(); + if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.CodeType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.StringType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.StringType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.UriType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UriType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DateType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.OidType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.OidType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UuidType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.IdType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IdType) fixed).getValue()); + else if (fixed instanceof Quantity) + checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); + else if (fixed instanceof Address) + checkAddress(errors, path, focus, (Address) fixed, fixedSource, pattern); + else if (fixed instanceof ContactPoint) + checkContactPoint(errors, path, focus, (ContactPoint) fixed, fixedSource, pattern); + else if (fixed instanceof Attachment) + checkAttachment(errors, path, focus, (Attachment) fixed, fixedSource, pattern); + else if (fixed instanceof Identifier) + checkIdentifier(errors, path, focus, (Identifier) fixed, fixedSource, pattern); + else if (fixed instanceof Coding) + checkCoding(errors, path, focus, (Coding) fixed, fixedSource, pattern); + else if (fixed instanceof HumanName) + checkHumanName(errors, path, focus, (HumanName) fixed, fixedSource, pattern); + else if (fixed instanceof CodeableConcept) + checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed, fixedSource, pattern); + else if (fixed instanceof Timing) + checkTiming(errors, path, focus, (Timing) fixed, fixedSource, pattern); + else if (fixed instanceof Period) + checkPeriod(errors, path, focus, (Period) fixed, fixedSource, pattern); + else if (fixed instanceof Range) + checkRange(errors, path, focus, (Range) fixed, fixedSource, pattern); + else if (fixed instanceof Ratio) + checkRatio(errors, path, focus, (Ratio) fixed, fixedSource, pattern); + else if (fixed instanceof SampledData) + checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); + + else + rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false,messages.getString("Unhandled_fixed_value_type_"), fixed.getClass().getName()); + List extensions = new ArrayList(); + focus.getNamedChildren("extension", extensions); + if (fixed.getExtension().size() == 0) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0,messages.getString("No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions")); + } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(),messages.getString("Extensions_count_mismatch_expected__but_found_"), Integer.toString(fixed.getExtension().size()), Integer.toString(extensions.size()))) { + for (Extension e : fixed.getExtension()) { + Element ex = getExtensionByUrl(extensions, e.getUrl()); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null,messages.getString("Extension_count_mismatch_unable_to_find_extension_"), e.getUrl())) { + checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); + } + } + } + } + } + + private void checkHumanName(List errors, String path, Element focus, HumanName fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + + List parts = new ArrayList(); + focus.getNamedChildren("family", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(),messages.getString("Expected__but_found__family_elements"), (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); + } + focus.getNamedChildren("given", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(),messages.getString("Expected__but_found__given_elements"), Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); + } + focus.getNamedChildren("prefix", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(),messages.getString("Expected__but_found__prefix_elements"), Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); + } + focus.getNamedChildren("suffix", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(),messages.getString("Expected__but_found__suffix_elements"), Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); + } + } + + private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { + String system = element.getNamedChildValue("system"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Identifiersystem_must_be_an_absolute_reference_not_a_local_reference")); + } + + private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".type", focus.getNamedChild("type"), fixed.getType(), fixedSource, "type", focus, pattern); + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + checkFixedValue(errors, path + ".assigner", focus.getNamedChild("assigner"), fixed.getAssigner(), fixedSource, "assigner", focus, pattern); + } + + private void checkPeriod(List errors, String path, Element focus, Period fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".start", focus.getNamedChild("start"), fixed.getStartElement(), fixedSource, "start", focus, pattern); + checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), fixedSource, "end", focus, pattern); + } + + private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { + if (isBlank(e.primitiveValue())) { + if (e.primitiveValue() == null) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_or_must_have_child_extensions")); + else if (e.primitiveValue().length() == 0) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_that_is_not_empty")); + else if (StringUtils.isWhitespace(e.primitiveValue())) + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_should_not_only_be_whitespace")); + return; + } + String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); + if (regex != null) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex),messages.getString("Element_value__does_not_meet_regex_"), e.primitiveValue(), regex); + + if (type.equals("boolean")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()),messages.getString("boolean_values_must_be_true_or_false")); + } + if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { + String url = e.primitiveValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"),messages.getString("URI_values_cannot_start_with_oid")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"),messages.getString("URI_values_cannot_start_with_uuid")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) // work around an old invalid example in a core package || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url),messages.getString("URI_values_cannot_have_whitespace"), url); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + + if (type.equals("oid")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"),messages.getString("OIDs_must_start_with_urnoid"))) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)),messages.getString("OIDs_must_be_valid")); + } + if (type.equals("uuid")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"),messages.getString("UUIDs_must_start_with_urnuuid")); + try { + UUID.fromString(url.substring(8)); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("UUIDs_must_be_valid_"), ex.getMessage()); + } + } + + // now, do we check the URI target? + if (fetcher != null) { + boolean found; + try { + found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url); + } catch (IOException e1) { + found = false; + } + rule(errors, IssueType.INVALID, e.line(), e.col(), path, found,messages.getString("URL_value__does_not_resolve"), url); + } + } + if (type.equals("id")) { + // work around an old issue with ElementDefinition.id + if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()),messages.getString("id_value__is_not_valid"), e.primitiveValue()); + } + } + if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0,messages.getString("value_cannot_be_empty"))) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()),messages.getString("value_should_not_start_or_finish_with_whitespace")); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576,messages.getString("value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes"))) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + } + } + } + if (type.equals("dateTime")) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"),messages.getString("Not_a_valid_date_time")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()),messages.getString("if_a_date_has_a_time_it_must_have_a_timezone")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + try { + DateTimeType dt = new DateTimeType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_datetime_"), ex.getMessage()); + } + } + if (type.equals("time")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"),messages.getString("Not_a_valid_time")); + try { + TimeType dt = new TimeType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_time_"), ex.getMessage()); + } + } + if (type.equals("date")) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"),messages.getString("Not_a_valid_date")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_value_of_"), context.getMaxLength()); + try { + DateType dt = new DateType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_date_"), ex.getMessage()); + } + } + if (type.equals("base64Binary")) { + String encoded = e.primitiveValue(); + if (isNotBlank(encoded)) { + /* + * Technically this is not bulletproof as some invalid base64 won't be caught, + * but I think it's good enough. The original code used Java8 Base64 decoder + * but I've replaced it with a regex for 2 reasons: + * 1. This code will run on any version of Java + * 2. This code doesn't actually decode, which is much easier on memory use for big payloads + */ + int charCount = 0; + for (int i = 0; i < encoded.length(); i++) { + char nextChar = encoded.charAt(i); + if (Character.isWhitespace(nextChar)) { + continue; + } + if (Character.isLetterOrDigit(nextChar)) { + charCount++; + } + if (nextChar == '/' || nextChar == '=' || nextChar == '+') { + charCount++; + } + } + + if (charCount > 0 && charCount % 4 != 0) { + String value = encoded.length() < 100 ? encoded : "(snip)"; + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("The_value_0_is_not_a_valid_Base64_value"), value); + } + } + } + if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer"), e.primitiveValue())) { + Integer v = new Integer(e.getValue()).intValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); + if (type.equals("unsignedInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); + if (type.equals("positiveInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); + } + } + if (type.equals("integer64")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer64"), e.primitiveValue())) { + Long v = new Long(e.getValue()).longValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); + if (type.equals("unsignedInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); + if (type.equals("positiveInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); + } + } + if (type.equals("decimal")) { + if (e.primitiveValue() != null) { + DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE,messages.getString("The_value__is_not_a_valid_decimal"), e.primitiveValue())) + warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE,messages.getString("The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals"), e.primitiveValue()); + } + } + if (type.equals("instant")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"),messages.getString("The_instant__is_not_valid_by_regex"), e.primitiveValue()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); + try { + InstantType dt = new InstantType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_instant_"), ex.getMessage()); + } + } + + if (type.equals("code") && e.primitiveValue() != null) { + // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace + // other than single spaces in the contents + rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()),messages.getString("The_code__is_not_valid_whitespace_rules"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + } + + if (context.hasBinding() && e.primitiveValue() != null) { + checkPrimitiveBinding(errors, path, type, context, e, profile, node); + } + + if (type.equals("xhtml")) { + XhtmlNode xhtml = e.getXhtml(); + if (xhtml != null) { // if it is null, this is an error already noted in the parsers + // check that the namespace is there and correct. + String ns = xhtml.getNsDecl(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); + // check that inner namespaces are all correct + checkInnerNS(errors, e, path, xhtml.getChildNodes()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()),messages.getString("Wrong_name_on_the_XHTML___must_start_with_div"), ns); + // check that no illegal elements and attributes have been used + checkInnerNames(errors, e, path, xhtml.getChildNodes()); + } + } + + if (context.hasFixed()) { + checkFixedValue(errors, path, e, context.getFixed(), profile.getUrl(), context.getSliceName(), null, false); + } + if (context.hasPattern()) { + checkFixedValue(errors, path, e, context.getPattern(), profile.getUrl(), context.getSliceName(), null, true); + } + + // for nothing to check + } + + private boolean isDefinitionURL(String url) { + return Utilities.existsInList(url, "http://hl7.org/fhirpath/System.Boolean", "http://hl7.org/fhirpath/System.String", "http://hl7.org/fhirpath/System.Integer", + "http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity"); + } + + private void checkInnerNames(List errors, Element e, String path, List list) { + for (XhtmlNode node : list) { + if (node.getNodeType() == NodeType.Element) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", "code", "samp", "img", "map", "area" ),messages.getString("Illegal_element_name_in_the_XHTML_"), node.getName()); + for (String an : node.getAttributes().keySet()) { + boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, + "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", + // tables + "span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") || + + Utilities.existsInList(node.getName() + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite", + "a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src", + "img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape", + "area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border", + "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" + ); + if (!ok) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Illegal_attribute_name_in_the_XHTML__on_"), an, node.getName()); + } + checkInnerNames(errors, e, path, node.getChildNodes()); + } + } + } + + private void checkInnerNS(List errors, Element e, String path, List list) { + for (XhtmlNode node : list) { + if (node.getNodeType() == NodeType.Element) { + String ns = node.getNsDecl(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); + checkInnerNS(errors, e, path, node.getChildNodes()); + } + } + } + + private void checkPrimitiveBinding(List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { + // We ignore bindings that aren't on string, uri or code + if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { + return; + } + if (noTerminologyChecks) + return; + + String value = element.primitiveValue(); + // System.out.println("check "+value+" in "+path); + + // firstly, resolve the value set + ElementDefinitionBindingComponent binding = elementContext.getBinding(); + if (binding.hasValueSet()) { + ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null,messages.getString("ValueSet_0_not_found_by_validator"), describeReference(binding.getValueSet()))) { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), value, vs); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server"), value); + else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)" + getErrorMessage(vr.getMessage())); + 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) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + } + } + } + } + } else if (!noBindingMsgSuppressed) + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"),messages.getString("Binding_has_no_source_so_cant_be_checked")); + } + + private void checkQuantity(List 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); + checkFixedValue(errors, path + ".units", focus.getNamedChild("unit"), fixed.getUnitElement(), fixedSource, "units", focus, pattern); + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); + } + + // implementation + + private void checkRange(List errors, String path, Element focus, Range fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".low", focus.getNamedChild("low"), fixed.getLow(), fixedSource, "low", focus, pattern); + checkFixedValue(errors, path + ".high", focus.getNamedChild("high"), fixed.getHigh(), fixedSource, "high", focus, pattern); + + } + + private void checkRatio(List errors, String path, Element focus, Ratio fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".numerator", focus.getNamedChild("numerator"), fixed.getNumerator(), fixedSource, "numerator", focus, pattern); + checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), fixedSource, "denominator", focus, pattern); + } + + private void checkReference(ValidatorHostContext hostContext, List errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException { + Reference reference = ObjectConverter.readAsReference(element); + + String ref = reference.getReference(); + if (Utilities.noString(ref)) { + if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")),messages.getString("A_Reference_without_an_actual_reference_or_identifier_should_have_a_display")); + } + return; + } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { + // special known URLs that can't be validated but are known to be valid + return; + } + + ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); + String refType; + if (ref.startsWith("#")) { + refType = "contained"; + } else { + if (we == null) { + refType = "remote"; + } else { + refType = "bundled"; + } + } + ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundled") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.getAppContext(), path, ref); + + if (pol.checkExists()) { + if (we == null) { + if (fetcher == null) { + if (!refType.equals("contained")) + throw new FHIRException("Resource resolution services not provided"); + } else { + Element ext = null; + if (fetchCache.containsKey(ref)) { + ext = fetchCache.get(ref); + } else { + try { + ext = fetcher.fetch(hostContext.getAppContext(), ref); + } catch (IOException e) { + throw new FHIRException(e); + } + if (ext != null) { + fetchCache.put(ref, ext); + } + } + we = ext == null ? null : makeExternalRef(ext, path); + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS),messages.getString("Unable_to_resolve_resource_"), ref); + } + + String ft; + if (we != null) + ft = we.getType(); + else + ft = tryParse(ref); + + if (reference.hasType()) { // R4 onwards... + // the type has to match the specified + String tu = isAbsolute(reference.getType()) ? reference.getType() : "http://hl7.org/fhir/StructureDefinition/" + reference.getType(); + TypeRefComponent containerType = container.getType("Reference"); + if (!containerType.hasTargetProfile(tu) && !containerType.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { + boolean matchingResource = false; + for (CanonicalType target : containerType.getTargetProfile()) { + StructureDefinition sd = resolveProfile(profile, target.asStringValue()); + if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) { + matchingResource = true; + break; + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource,messages.getString("The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_"), reference.getType(), container.getType("Reference").getTargetProfile()); + + } + // the type has to match the actual + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()),messages.getString("The_specified_type__does_not_match_the_found_type_"), reference.getType(), ft); + } + + if (we != null && pol.checkType()) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null,messages.getString("Unable_to_determine_type_of_target_resource"))) { + // we validate as much as we can. First, can we infer a type from the profile? + boolean ok = false; + TypeRefComponent type = getReferenceTypeRef(container.getType()); + if (type.hasTargetProfile() && !type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { + Set types = new HashSet<>(); + List profiles = new ArrayList<>(); + for (UriType u : type.getTargetProfile()) { + StructureDefinition sd = resolveProfile(profile, u.getValue()); + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null,messages.getString("Unable_to_resolve_the_profile_reference_"), u.getValue())) { + types.add(sd.getType()); + if (ft.equals(sd.getType())) { + ok = true; + profiles.add(sd); + } + } + } + if (!pol.checkValid()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0,messages.getString("Unable_to_find_matching_profile_for__by_type_among_choices_"), ref, StringUtils.join("; ", type.getTargetProfile())); + } else { + Map> badProfiles = new HashMap>(); + Map> goodProfiles = new HashMap>(); + int goodCount = 0; + for (StructureDefinition pr : profiles) { + List profileErrors = new ArrayList(); + validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack()); + if (!hasErrors(profileErrors)) { + goodCount++; + goodProfiles.put(pr, profileErrors); + trackUsage(pr, hostContext, element); + } else { + badProfiles.put(pr, profileErrors); + } + } + if (goodCount == 1) { + if (showMessagesFromReferences) { + for (ValidationMessage vm : goodProfiles.values().iterator().next()) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + + } else if (goodProfiles.size() == 0) { + if (!isShowMessagesFromReferences()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles),messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); + for (StructureDefinition sd : badProfiles.keySet()) { + slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); + } + } else { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1,messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); + for (List messages : badProfiles.values()) { + for (ValidationMessage vm : messages) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + } + } else { + if (!isShowMessagesFromReferences()) { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); + for (StructureDefinition sd : badProfiles.keySet()) { + slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); + } + } else { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); + for (List messages : goodProfiles.values()) { + for (ValidationMessage vm : messages) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + } + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok,messages.getString("Invalid_Resource_target_type_Found__but_expected_one_of_"), ft, types.toString()); + } + if (type.hasAggregation()) { + boolean modeOk = false; + for (Enumeration mode : type.getAggregation()) { + if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained")) + modeOk = true; + else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled")) + modeOk = true; + else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) + modeOk = true; + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk,messages.getString("Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference"), refType); + } + } + } + if (we == null) { + TypeRefComponent type = getReferenceTypeRef(container.getType()); + boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); + rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef,messages.getString("Bundled_or_contained_reference_not_found_within_the_bundleresource_"), ref); + } + if (we == null && ft != null && assumeValidRestReferences) { + // if we == null, we inferred ft from the reference. if we are told to treat this as gospel + TypeRefComponent type = getReferenceTypeRef(container.getType()); + Set types = new HashSet<>(); + for (CanonicalType tp : type.getTargetProfile()) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue()); + if (sd != null) { + types.add(sd.getType()); + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft),messages.getString("The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_"), ft, ref, types); + + } + if (pol == ReferenceValidationPolicy.CHECK_VALID) { + // todo.... + } + } + + private String asListByUrl(Collection list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (StructureDefinition sd : list) { + b.append(sd.getUrl()); + } + return b.toString(); + } + + private String asList(Collection list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (CanonicalType c : list) { + b.append(c.getValue()); + } + return b.toString(); + } + + private boolean areAllBaseProfiles(List profiles) { + for (StructureDefinition sd : profiles) { + if (!sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { + return false; + } + } + return true; + } + + private String errorSummaryForSlicing(List list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ValidationMessage vm : list) { + if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL || vm.isSlicingHint()) { + b.append(vm.getLocation() + ": " + vm.getMessage()); + } + } + return b.toString(); + } + + private String errorSummaryForSlicingAsHtml(List list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ValidationMessage vm : list) { + if (vm.isSlicingHint()) { + b.append("
  • " + vm.getLocation() + ": " + vm.getSliceHtml() + "
  • "); + } else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) { + b.append("
  • " + vm.getLocation() + ": " + vm.getHtml() + "
  • "); + } + } + return "
      " + b.toString() + "
    "; + } + + private TypeRefComponent getReferenceTypeRef(List types) { + for (TypeRefComponent tr : types) { + if ("Reference".equals(tr.getCode())) { + return tr; + } + } + return null; + } + + private String checkResourceType(String type) { + long t = System.nanoTime(); + try { + if (context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + type) != null) + return type; + else + return null; + } finally { + sdTime = sdTime + (System.nanoTime() - t); + } + } + + private void checkSampledData(List errors, String path, Element focus, SampledData fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".origin", focus.getNamedChild("origin"), fixed.getOrigin(), fixedSource, "origin", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriodElement(), fixedSource, "period", focus, pattern); + checkFixedValue(errors, path + ".factor", focus.getNamedChild("factor"), fixed.getFactorElement(), fixedSource, "factor", focus, pattern); + checkFixedValue(errors, path + ".lowerLimit", focus.getNamedChild("lowerLimit"), fixed.getLowerLimitElement(), fixedSource, "lowerLimit", focus, pattern); + checkFixedValue(errors, path + ".upperLimit", focus.getNamedChild("upperLimit"), fixed.getUpperLimitElement(), fixedSource, "upperLimit", focus, pattern); + checkFixedValue(errors, path + ".dimensions", focus.getNamedChild("dimensions"), fixed.getDimensionsElement(), fixedSource, "dimensions", focus, pattern); + checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); + } + + private void checkTiming(List errors, String path, Element focus, Timing fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".repeat", focus.getNamedChild("repeat"), fixed.getRepeat(), fixedSource, "value", focus, pattern); + + List events = new ArrayList(); + focus.getNamedChildren("event", events); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(),messages.getString("Expected__but_found__event_elements"), Integer.toString(fixed.getEvent().size()), Integer.toString(events.size()))) { + for (int i = 0; i < events.size(); i++) + checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); + } + } + + private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) { + for (ValueSetExpansionContainsComponent c : cnt.getContains()) { + if (code.equals(c.getCode()) && system.equals(c.getSystem().toString())) + return true; + if (codeinExpansion(c, system, code)) return true; } + return false; + } - private String errorSummaryForSlicing(List list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (ValidationMessage vm : list) { - if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL || vm.isSlicingHint()) { - b.append(vm.getLocation() + ": " + vm.getMessage()); - } + private boolean codeInExpansion(ValueSet vs, String system, String code) { + for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { + if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem()))) + return true; + if (codeinExpansion(c, system, code)) + return true; + } + return false; + } + + private String describeReference(String reference) { + if (reference == null) + return "null"; + return reference; + } + + private String describeReference(String reference, CanonicalResource target) { + if (reference == null && target == null) + return "null"; + if (reference == null) { + return target.getUrl(); + } + if (target == null) { + return reference; + } + if (reference.equals(target.getUrl())) { + return reference; + } + return reference + "(which actually refers to " + target.getUrl() + ")"; + } + + private String describeTypes(List types) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (TypeRefComponent t : types) { + b.append(t.getWorkingCode()); + } + return b.toString(); + } + + protected ElementDefinition findElement(StructureDefinition profile, String name) { + for (ElementDefinition c : profile.getSnapshot().getElement()) { + if (c.getPath().equals(name)) { + return c; + } + } + return null; + } + + public BestPracticeWarningLevel getBestPracticeWarningLevel() { + return bpWarnings; + } + + @Override + public CheckDisplayOption getCheckDisplay() { + return checkDisplay; + } + + private ConceptDefinitionComponent getCodeDefinition(ConceptDefinitionComponent c, String code) { + if (code.equals(c.getCode())) + return c; + for (ConceptDefinitionComponent g : c.getConcept()) { + ConceptDefinitionComponent r = getCodeDefinition(g, code); + if (r != null) + return r; + } + return null; + } + + private ConceptDefinitionComponent getCodeDefinition(CodeSystem cs, String code) { + for (ConceptDefinitionComponent c : cs.getConcept()) { + ConceptDefinitionComponent r = getCodeDefinition(c, code); + if (r != null) + return r; + } + return null; + } + + private IndexedElement getContainedById(Element container, String id) { + List contained = new ArrayList(); + container.getNamedChildren("contained", contained); + for (int i = 0; i < contained.size(); i++) { + Element we = contained.get(i); + if (id.equals(we.getNamedChildValue("id"))) { + return new IndexedElement(i, we, null); + } + } + return null; + } + + public IWorkerContext getContext() { + return context; + } + + private List getCriteriaForDiscriminator(String path, ElementDefinition element, String discriminator, StructureDefinition profile, boolean removeResolve) throws FHIRException { + List elements = new ArrayList(); + if ("value".equals(discriminator) && element.hasFixed()) { + elements.add(element); + return elements; + } + + if (removeResolve) { // if we're doing profile slicing, we don't want to walk into the last resolve.. we need the profile on the source not the target + if (discriminator.equals("resolve()")) { + elements.add(element); + return elements; + } + if (discriminator.endsWith(".resolve()")) + discriminator = discriminator.substring(0, discriminator.length() - 10); + } + + ElementDefinition ed = null; + ExpressionNode expr = fpe.parse(fixExpr(discriminator)); + long t2 = System.nanoTime(); + ed = fpe.evaluateDefinition(expr, profile, element); + sdTime = sdTime + (System.nanoTime() - t2); + if (ed != null) + elements.add(ed); + + for (TypeRefComponent type : element.getType()) { + for (CanonicalType p : type.getProfile()) { + String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; + StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); + if (sd == null) + throw new DefinitionException("Unable to resolve profile " + p); + profile = sd; + if (id == null) + element = sd.getSnapshot().getElementFirstRep(); + else { + element = null; + for (ElementDefinition t : sd.getSnapshot().getElement()) { + if (id.equals(t.getId())) + element = t; + } + if (element == null) + throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); } - return b.toString(); - } - - private String errorSummaryForSlicingAsHtml(List list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (ValidationMessage vm : list) { - if (vm.isSlicingHint()) { - b.append("
  • " + vm.getLocation() + ": " + vm.getSliceHtml() + "
  • "); - } else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) { - b.append("
  • " + vm.getLocation() + ": " + vm.getHtml() + "
  • "); - } - } - return "
      " + b.toString() + "
    "; - } - - private TypeRefComponent getReferenceTypeRef(List types) { - for (TypeRefComponent tr : types) { - if ("Reference".equals(tr.getCode())) { - return tr; - } - } - return null; - } - - private String checkResourceType(String type) { - long t = System.nanoTime(); - try { - if (context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + type) != null) - return type; - else - return null; - } finally { - sdTime = sdTime + (System.nanoTime() - t); - } - } - - private void checkSampledData(List errors, String path, Element focus, SampledData fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".origin", focus.getNamedChild("origin"), fixed.getOrigin(), fixedSource, "origin", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriodElement(), fixedSource, "period", focus, pattern); - checkFixedValue(errors, path + ".factor", focus.getNamedChild("factor"), fixed.getFactorElement(), fixedSource, "factor", focus, pattern); - checkFixedValue(errors, path + ".lowerLimit", focus.getNamedChild("lowerLimit"), fixed.getLowerLimitElement(), fixedSource, "lowerLimit", focus, pattern); - checkFixedValue(errors, path + ".upperLimit", focus.getNamedChild("upperLimit"), fixed.getUpperLimitElement(), fixedSource, "upperLimit", focus, pattern); - checkFixedValue(errors, path + ".dimensions", focus.getNamedChild("dimensions"), fixed.getDimensionsElement(), fixedSource, "dimensions", focus, pattern); - checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); - } - - private void checkTiming(List errors, String path, Element focus, Timing fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".repeat", focus.getNamedChild("repeat"), fixed.getRepeat(), fixedSource, "value", focus, pattern); - - List events = new ArrayList(); - focus.getNamedChildren("event", events); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(), - "Expected " + Integer.toString(fixed.getEvent().size()) + " but found " + Integer.toString(events.size()) + " event elements")) { - for (int i = 0; i < events.size(); i++) - checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); - } - } - - private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) { - for (ValueSetExpansionContainsComponent c : cnt.getContains()) { - if (code.equals(c.getCode()) && system.equals(c.getSystem().toString())) - return true; - if (codeinExpansion(c, system, code)) - return true; - } - return false; - } - - private boolean codeInExpansion(ValueSet vs, String system, String code) { - for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { - if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem()))) - return true; - if (codeinExpansion(c, system, code)) - return true; - } - return false; - } - - private String describeReference(String reference) { - if (reference == null) - return "null"; - return reference; - } - - private String describeReference(String reference, CanonicalResource target) { - if (reference == null && target == null) - return "null"; - if (reference == null) { - return target.getUrl(); - } - if (target == null) { - return reference; - } - if (reference.equals(target.getUrl())) { - return reference; - } - return reference + "(which actually refers to " + target.getUrl() + ")"; - } - - private String describeTypes(List types) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (TypeRefComponent t : types) { - b.append(t.getWorkingCode()); - } - return b.toString(); - } - - protected ElementDefinition findElement(StructureDefinition profile, String name) { - for (ElementDefinition c : profile.getSnapshot().getElement()) { - if (c.getPath().equals(name)) { - return c; - } - } - return null; - } - - public BestPracticeWarningLevel getBestPracticeWarningLevel() { - return bpWarnings; - } - - @Override - public CheckDisplayOption getCheckDisplay() { - return checkDisplay; - } - - private ConceptDefinitionComponent getCodeDefinition(ConceptDefinitionComponent c, String code) { - if (code.equals(c.getCode())) - return c; - for (ConceptDefinitionComponent g : c.getConcept()) { - ConceptDefinitionComponent r = getCodeDefinition(g, code); - if (r != null) - return r; - } - return null; - } - - private ConceptDefinitionComponent getCodeDefinition(CodeSystem cs, String code) { - for (ConceptDefinitionComponent c : cs.getConcept()) { - ConceptDefinitionComponent r = getCodeDefinition(c, code); - if (r != null) - return r; - } - return null; - } - - private IndexedElement getContainedById(Element container, String id) { - List contained = new ArrayList(); - container.getNamedChildren("contained", contained); - for (int i = 0; i < contained.size(); i++) { - Element we = contained.get(i); - if (id.equals(we.getNamedChildValue("id"))) { - return new IndexedElement(i, we, null); - } - } - return null; - } - - public IWorkerContext getContext() { - return context; - } - - private List getCriteriaForDiscriminator(String path, ElementDefinition element, String discriminator, StructureDefinition profile, boolean removeResolve) throws FHIRException { - List elements = new ArrayList(); - if ("value".equals(discriminator) && element.hasFixed()) { - elements.add(element); - return elements; - } - - if (removeResolve) { // if we're doing profile slicing, we don't want to walk into the last resolve.. we need the profile on the source not the target - if (discriminator.equals("resolve()")) { - elements.add(element); - return elements; - } - if (discriminator.endsWith(".resolve()")) - discriminator = discriminator.substring(0, discriminator.length() - 10); - } - - ElementDefinition ed = null; - ExpressionNode expr = fpe.parse(fixExpr(discriminator)); - long t2 = System.nanoTime(); + expr = fpe.parse(fixExpr(discriminator)); + t2 = System.nanoTime(); ed = fpe.evaluateDefinition(expr, profile, element); sdTime = sdTime + (System.nanoTime() - t2); if (ed != null) - elements.add(ed); + elements.add(ed); + } + } + return elements; + } - for (TypeRefComponent type : element.getType()) { - for (CanonicalType p : type.getProfile()) { - String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; - StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); - if (sd == null) - throw new DefinitionException("Unable to resolve profile " + p); - profile = sd; - if (id == null) - element = sd.getSnapshot().getElementFirstRep(); - else { - element = null; - for (ElementDefinition t : sd.getSnapshot().getElement()) { - if (id.equals(t.getId())) - element = t; - } - if (element == null) - throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); - } - expr = fpe.parse(fixExpr(discriminator)); - t2 = System.nanoTime(); - ed = fpe.evaluateDefinition(expr, profile, element); - sdTime = sdTime + (System.nanoTime() - t2); - if (ed != null) - elements.add(ed); - } + + private Element getExtensionByUrl(List extensions, String urlSimple) { + for (Element e : extensions) { + if (urlSimple.equals(e.getNamedChildValue("url"))) + return e; + } + return null; + } + + public List getExtensionDomains() { + return extensionDomains; + } + + private IndexedElement getFromBundle(Element bundle, String ref, String fullUrl, List errors, String path, String type, boolean isTransaction) { + String targetUrl = null; + String version = ""; + String resourceType = null; + if (ref.startsWith("http") || ref.startsWith("urn")) { + // We've got an absolute reference, no need to calculate + if (ref.contains("/_history/")) { + targetUrl = ref.substring(0, ref.indexOf("/_history/") - 1); + version = ref.substring(ref.indexOf("/_history/") + 10); + } else + targetUrl = ref; + + } else if (fullUrl == null) { + //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error + // but this rule doesn't apply for batches or transactions + rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"),messages.getString("Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl")); + return null; + + } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { + if (isTransaction) { + rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref),messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_"), ref); + } else { + rule(errors, IssueType.INVALID, -1, -1, path, false,messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_"), ref); + } + return null; + + } else { + String base = ""; + if (fullUrl.startsWith("urn")) { + String[] parts = fullUrl.split("\\:"); + for (int i = 0; i < parts.length - 1; i++) { + base = base + parts[i] + ":"; } - return elements; - } - - - private Element getExtensionByUrl(List extensions, String urlSimple) { - for (Element e : extensions) { - if (urlSimple.equals(e.getNamedChildValue("url"))) - return e; + } else { + String[] parts; + parts = fullUrl.split("/"); + for (int i = 0; i < parts.length - 2; i++) { + base = base + parts[i] + "/"; } - return null; + } + + String id = null; + if (ref.contains("/_history/")) { + version = ref.substring(ref.indexOf("/_history/") + 10); + String[] refBaseParts = ref.substring(0, ref.indexOf("/_history/")).split("/"); + resourceType = refBaseParts[0]; + id = refBaseParts[1]; + } else if (base.startsWith("urn")) { + resourceType = ref.split("/")[0]; + id = ref.split("/")[1]; + } else + id = ref; + + targetUrl = base + id; } - public List getExtensionDomains() { - return extensionDomains; - } - - private IndexedElement getFromBundle(Element bundle, String ref, String fullUrl, List errors, String path, String type, boolean isTransaction) { - String targetUrl = null; - String version = ""; - String resourceType = null; - if (ref.startsWith("http") || ref.startsWith("urn")) { - // We've got an absolute reference, no need to calculate - if (ref.contains("/_history/")) { - targetUrl = ref.substring(0, ref.indexOf("/_history/") - 1); - version = ref.substring(ref.indexOf("/_history/") + 10); - } else - targetUrl = ref; - - } else if (fullUrl == null) { - //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error - // but this rule doesn't apply for batches or transactions - rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"), "Relative Reference appears inside Bundle whose entry is missing a fullUrl"); - return null; - - } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { - if (isTransaction) { - rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref + ")"); - } else { - rule(errors, IssueType.INVALID, -1, -1, path, false, "Relative URLs must be of the format [ResourceName]/[id]. Encountered " + ref); - } - return null; - + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + Element match = null; + int matchIndex = -1; + for (int i = 0; i < entries.size(); i++) { + Element we = entries.get(i); + if (targetUrl.equals(we.getChildValue("fullUrl"))) { + Element r = we.getNamedChild("resource"); + if (version.isEmpty()) { + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); + match = r; + matchIndex = i; } else { - String base = ""; - if (fullUrl.startsWith("urn")) { - String[] parts = fullUrl.split("\\:"); - for (int i = 0; i < parts.length - 1; i++) { - base = base + parts[i] + ":"; - } - } else { - String[] parts; - parts = fullUrl.split("/"); - for (int i = 0; i < parts.length - 2; i++) { - base = base + parts[i] + "/"; - } + try { + if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); + match = r; + matchIndex = i; } - - String id = null; - if (ref.contains("/_history/")) { - version = ref.substring(ref.indexOf("/_history/") + 10); - String[] refBaseParts = ref.substring(0, ref.indexOf("/_history/")).split("/"); - resourceType = refBaseParts[0]; - id = refBaseParts[1]; - } else if (base.startsWith("urn")) { - resourceType = ref.split("/")[0]; - id = ref.split("/")[1]; - } else - id = ref; - - targetUrl = base + id; + } catch (Exception e) { + warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null,messages.getString("Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references"), targetUrl); + // If one of these things is null + } } - - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); - Element match = null; - int matchIndex = -1; - for (int i = 0; i < entries.size(); i++) { - Element we = entries.get(i); - if (targetUrl.equals(we.getChildValue("fullUrl"))) { - Element r = we.getNamedChild("resource"); - if (version.isEmpty()) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); - match = r; - matchIndex = i; - } else { - try { - if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); - match = r; - matchIndex = i; - } - } catch (Exception e) { - warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null, "Entries matching fullURL " + targetUrl + " should declare meta/versionId because there are version-specific references"); - // If one of these things is null - } - } - } - } - - if (match != null && resourceType != null) - rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType), "Matching reference for reference " + ref + " has resourceType " + match.getType()); - if (match == null) - warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"), "URN reference is not locally contained within the bundle " + ref); - return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); + } } - private boolean isSearchUrl(String ref) { - if (Utilities.noString(ref) || !ref.contains("?")) { - return false; - } - String tn = ref.substring(0, ref.indexOf("?")); - String q = ref.substring(ref.indexOf("?") + 1); - if (!context.getResourceNames().contains(tn)) { - return false; - } else { - return q.matches("([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+)(&([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+))*"); - } - } + if (match != null && resourceType != null) + rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType),messages.getString("Matching_reference_for_reference__has_resourceType_"), ref, match.getType()); + if (match == null) + warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"),messages.getString("URN_reference_is_not_locally_contained_within_the_bundle_"), ref); + return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); + } - private StructureDefinition getProfileForType(String type, List list) { - for (TypeRefComponent tr : list) { - String url = tr.getWorkingCode(); - if (!Utilities.isAbsoluteUrl(url)) - url = "http://hl7.org/fhir/StructureDefinition/" + url; - long t = System.nanoTime(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - sdTime = sdTime + (System.nanoTime() - t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) - return sd; - } - return null; + private boolean isSearchUrl(String ref) { + if (Utilities.noString(ref) || !ref.contains("?")) { + return false; } + String tn = ref.substring(0, ref.indexOf("?")); + String q = ref.substring(ref.indexOf("?") + 1); + if (!context.getResourceNames().contains(tn)) { + return false; + } else { + return q.matches("([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+)(&([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+))*"); + } + } - private Element getValueForDiscriminator(Object appContext, List errors, Element element, String discriminator, ElementDefinition criteria, NodeStack stack) throws FHIRException, IOException { - String p = stack.getLiteralPath() + "." + element.getName(); - Element focus = element; - String[] dlist = discriminator.split("\\."); - for (String d : dlist) { - if (focus.fhirType().equals("Reference") && d.equals("reference")) { - String url = focus.getChildValue("reference"); - if (Utilities.noString(url)) - throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? - Element target = resolve(appContext, url, stack, errors, p); - if (target == null) - throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - focus = target; - } else if (d.equals("value") && focus.isPrimitive()) { - return focus; - } else { - List children = focus.getChildren(d); - if (children.isEmpty()) - throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - if (children.size() > 1) - throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - focus = children.get(0); - p = p + "." + d; - } - } + private StructureDefinition getProfileForType(String type, List list) { + for (TypeRefComponent tr : list) { + String url = tr.getWorkingCode(); + if (!Utilities.isAbsoluteUrl(url)) + url = "http://hl7.org/fhir/StructureDefinition/" + url; + long t = System.nanoTime(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + sdTime = sdTime + (System.nanoTime() - t); + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + return sd; + } + return null; + } + + private Element getValueForDiscriminator(Object appContext, List errors, Element element, String discriminator, ElementDefinition criteria, NodeStack stack) throws FHIRException, IOException { + String p = stack.getLiteralPath() + "." + element.getName(); + Element focus = element; + String[] dlist = discriminator.split("\\."); + for (String d : dlist) { + if (focus.fhirType().equals("Reference") && d.equals("reference")) { + String url = focus.getChildValue("reference"); + if (Utilities.noString(url)) + throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? + Element target = resolve(appContext, url, stack, errors, p); + if (target == null) + throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + focus = target; + } else if (d.equals("value") && focus.isPrimitive()) { return focus; + } else { + List children = focus.getChildren(d); + if (children.isEmpty()) + throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + if (children.size() > 1) + throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + focus = children.get(0); + p = p + "." + d; + } } + return focus; + } - private CodeSystem getCodeSystem(String system) { - long t = System.nanoTime(); - try { - return context.fetchCodeSystem(system); - } finally { - txTime = txTime + (System.nanoTime() - t); + private CodeSystem getCodeSystem(String system) { + long t = System.nanoTime(); + try { + return context.fetchCodeSystem(system); + } finally { + txTime = txTime + (System.nanoTime() - t); + } + } + + private boolean hasTime(String fmt) { + return fmt.contains("T"); + } + + private boolean hasTimeZone(String fmt) { + return fmt.length() > 10 && (fmt.substring(10).contains("-") || fmt.substring(10).contains("+") || fmt.substring(10).contains("Z")); + } + + private boolean isAbsolute(String uri) { + return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:uuid:") || uri.startsWith("urn:oid:") || uri.startsWith("urn:ietf:") + || uri.startsWith("urn:iso:") || uri.startsWith("urn:iso-astm:") || isValidFHIRUrn(uri); + } + + private boolean isValidFHIRUrn(String uri) { + return (uri.equals("urn:x-fhir:uk:id:nhs-number")) || uri.startsWith("urn:"); // Anyone can invent a URN, so why should we complain? + } + + public boolean isAnyExtensionsAllowed() { + return anyExtensionsAllowed; + } + + public boolean isErrorForUnknownProfiles() { + return errorForUnknownProfiles; + } + + public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) { + this.errorForUnknownProfiles = errorForUnknownProfiles; + } + + private boolean isParametersEntry(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("resource") && (pathEntryHasName(parts[parts.length - 2], "parameter") || pathEntryHasName(parts[parts.length - 2], "part")); + } + + private boolean isBundleEntry(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("resource") && pathEntryHasName(parts[parts.length - 2], "entry"); + } + + private boolean isBundleOutcome(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("outcome") && pathEntryHasName(parts[parts.length - 2], "response"); + } + + + private static boolean pathEntryHasName(String thePathEntry, String theName) { + if (thePathEntry.equals(theName)) { + return true; + } + if (thePathEntry.length() >= theName.length() + 3) { + if (thePathEntry.startsWith(theName)) { + if (thePathEntry.charAt(theName.length()) == '[') { + return true; } + } } + return false; + } - private boolean hasTime(String fmt) { - return fmt.contains("T"); - } + public boolean isPrimitiveType(String code) { + StructureDefinition sd = context.fetchTypeDefinition(code); + return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; + } - private boolean hasTimeZone(String fmt) { - return fmt.length() > 10 && (fmt.substring(10).contains("-") || fmt.substring(10).contains("+") || fmt.substring(10).contains("Z")); - } + private String getErrorMessage(String message) { + return message != null ? " (error message = " + message + ")" : ""; + } - private boolean isAbsolute(String uri) { - return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:uuid:") || uri.startsWith("urn:oid:") || uri.startsWith("urn:ietf:") - || uri.startsWith("urn:iso:") || uri.startsWith("urn:iso-astm:") || isValidFHIRUrn(uri); - } + public boolean isSuppressLoincSnomedMessages() { + return suppressLoincSnomedMessages; + } - private boolean isValidFHIRUrn(String uri) { - return (uri.equals("urn:x-fhir:uk:id:nhs-number")) || uri.startsWith("urn:"); // Anyone can invent a URN, so why should we complain? - } + private boolean nameMatches(String name, String tail) { + if (tail.endsWith("[x]")) + return name.startsWith(tail.substring(0, tail.length() - 3)); + else + return (name.equals(tail)); + } - public boolean isAnyExtensionsAllowed() { - return anyExtensionsAllowed; - } - - public boolean isErrorForUnknownProfiles() { - return errorForUnknownProfiles; - } - - public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) { - this.errorForUnknownProfiles = errorForUnknownProfiles; - } - - private boolean isParametersEntry(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("resource") && (pathEntryHasName(parts[parts.length - 2], "parameter") || pathEntryHasName(parts[parts.length - 2], "part")); - } - - private boolean isBundleEntry(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("resource") && pathEntryHasName(parts[parts.length - 2], "entry"); - } - - private boolean isBundleOutcome(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("outcome") && pathEntryHasName(parts[parts.length - 2], "response"); - } - - - private static boolean pathEntryHasName(String thePathEntry, String theName) { - if (thePathEntry.equals(theName)) { - return true; - } - if (thePathEntry.length() >= theName.length() + 3) { - if (thePathEntry.startsWith(theName)) { - if (thePathEntry.charAt(theName.length()) == '[') { - return true; - } - } - } - return false; - } - - public boolean isPrimitiveType(String code) { - StructureDefinition sd = context.fetchTypeDefinition(code); - return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; - } - - private String getErrorMessage(String message) { - return message != null ? " (error message = " + message + ")" : ""; - } - - public boolean isSuppressLoincSnomedMessages() { - return suppressLoincSnomedMessages; - } - - private boolean nameMatches(String name, String tail) { - if (tail.endsWith("[x]")) - return name.startsWith(tail.substring(0, tail.length() - 3)); + private boolean passesCodeWhitespaceRules(String v) { + if (!v.trim().equals(v)) + return false; + boolean lastWasSpace = true; + for (char c : v.toCharArray()) { + if (c == ' ') { + if (lastWasSpace) + return false; else - return (name.equals(tail)); + lastWasSpace = true; + } else if (Character.isWhitespace(c)) + return false; + else + lastWasSpace = false; } + return true; + } - private boolean passesCodeWhitespaceRules(String v) { - if (!v.trim().equals(v)) - return false; - boolean lastWasSpace = true; - for (char c : v.toCharArray()) { - if (c == ' ') { - if (lastWasSpace) - return false; - else - lastWasSpace = true; - } else if (Character.isWhitespace(c)) - return false; - else - lastWasSpace = false; + private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element hostContext, Element source) { + if (ref.startsWith("#")) { + // work back through the parent list. + // 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 + while (stack != null && stack.getElement() != null) { + if (stack.getElement().getProperty().isResource()) { + // ok, we'll try to find the contained reference + IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); + if (res != null) { + ResolvedReference rr = new ResolvedReference(); + rr.setResource(stack.getElement()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } } - return true; - } - - private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element hostContext, Element source) { - if (ref.startsWith("#")) { - // work back through the parent list. - // 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 - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getProperty().isResource()) { - // ok, we'll try to find the contained reference - IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); - if (res != null) { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(stack.getElement()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY) { - return null; // we don't try to resolve contained references across this boundary - } - stack = stack.parent; - } + if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY) { + return null; // we don't try to resolve contained references across this boundary + } + stack = stack.parent; + } + return null; + } else { + // work back through the parent list - if any of them are bundles, try to resolve + // the resource in the bundle + String fullUrl = null; // we're going to try to work this out as we go up + while (stack != null && stack.getElement() != null) { + if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.parent != null && stack.parent.getElement().getName().equals("entry")) { + String type = stack.parent.parent.element.getChildValue("type"); + fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary + if (fullUrl == null) + rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null,messages.getString("Bundle_entry_missing_fullUrl")); + } + if ("Bundle".equals(stack.getElement().getType())) { + String type = stack.getElement().getChildValue("type"); + IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type)); + if (res == null) { return null; + } else { + ResolvedReference rr = new ResolvedReference(); + rr.setResource(res.getMatch()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), + res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, + res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } + } + stack = stack.parent; + } + // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. + if (hostContext != null && "Bundle".equals(hostContext.fhirType())) { + String type = hostContext.getChildValue("type"); + Element entry = getEntryForSource(hostContext, source); + fullUrl = entry.getChildValue("fullUrl"); + IndexedElement res = getFromBundle(hostContext, ref, fullUrl, errors, path, type, "transaction".equals(type)); + if (res == null) { + return null; } else { - // work back through the parent list - if any of them are bundles, try to resolve - // the resource in the bundle - String fullUrl = null; // we're going to try to work this out as we go up - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.parent != null && stack.parent.getElement().getName().equals("entry")) { - String type = stack.parent.parent.element.getChildValue("type"); - fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary - if (fullUrl == null) - rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), - Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, "Bundle entry missing fullUrl"); - } - if ("Bundle".equals(stack.getElement().getType())) { - String type = stack.getElement().getChildValue("type"); - IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type)); - if (res == null) { - return null; - } else { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(res.getMatch()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), - res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, - res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } - stack = stack.parent; - } - // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. - if (hostContext != null && "Bundle".equals(hostContext.fhirType())) { - String type = hostContext.getChildValue("type"); - Element entry = getEntryForSource(hostContext, source); - fullUrl = entry.getChildValue("fullUrl"); - IndexedElement res = getFromBundle(hostContext, ref, fullUrl, errors, path, type, "transaction".equals(type)); - if (res == null) { - return null; - } else { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(res.getMatch()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(new NodeStack(hostContext).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), - res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, - res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } + ResolvedReference rr = new ResolvedReference(); + rr.setResource(res.getMatch()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(new NodeStack(hostContext).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), + res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, + res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } + } + } + return null; + } + + private Element getEntryForSource(Element bundle, Element element) { + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + for (Element entry : entries) { + if (entry.hasDescendant(element)) { + return entry; + } + } + return null; + } + + private ResolvedReference makeExternalRef(Element external, String path) { + ResolvedReference res = new ResolvedReference(); + res.setResource(external); + res.setFocus(external); + res.setExternal(true); + res.setStack(new NodeStack(external, path)); + return res; + } + + + private Element resolve(Object appContext, String ref, NodeStack stack, List errors, String path) throws IOException, FHIRException { + Element local = localResolve(ref, stack, errors, path, null, null).getFocus(); + if (local != null) + return local; + if (fetcher == null) + return null; + if (fetchCache.containsKey(ref)) { + return fetchCache.get(ref); + } else { + Element res = fetcher.fetch(appContext, ref); + fetchCache.put(ref, res); + return res; + } + } + + private ValueSet resolveBindingReference(DomainResource ctxt, String reference, String uri) { + if (reference != null) { + if (reference.startsWith("#")) { + for (Resource c : ctxt.getContained()) { + if (c.getId().equals(reference.substring(1)) && (c instanceof ValueSet)) + return (ValueSet) c; } return null; - } + } else { + long t = System.nanoTime(); + ValueSet fr = context.fetchResource(ValueSet.class, reference); + if (fr == null) { + if (!Utilities.isAbsoluteUrl(reference)) { + reference = resolve(uri, reference); + fr = context.fetchResource(ValueSet.class, reference); + } + } + if (fr == null) + fr = ValueSetUtilities.generateImplicitValueSet(reference); + txTime = txTime + (System.nanoTime() - t); + return fr; + } + } else + return null; + } - private Element getEntryForSource(Element bundle, Element element) { - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); + private String resolve(String uri, String ref) { + if (isBlank(uri)) { + return ref; + } + String[] up = uri.split("\\/"); + String[] rp = ref.split("\\/"); + if (context.getResourceNames().contains(up[up.length - 2]) && context.getResourceNames().contains(rp[0])) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < up.length - 2; i++) { + b.append(up[i]); + b.append("/"); + } + b.append(ref); + return b.toString(); + } else + return ref; + } + + private Element resolveInBundle(List entries, String ref, String fullUrl, String type, String id) { + if (Utilities.isAbsoluteUrl(ref)) { + // if the reference is absolute, then you resolve by fullUrl. No other thinking is required. + for (Element entry : entries) { + String fu = entry.getNamedChildValue("fullUrl"); + if (ref.equals(fu)) + return entry; + } + return null; + } else { + // split into base, type, and id + String u = null; + if (fullUrl != null && fullUrl.endsWith(type + "/" + id)) + // fullUrl = complex + u = fullUrl.substring(0, fullUrl.length() - (type + "/" + id).length()) + ref; +// u = fullUrl.substring((type+"/"+id).length())+ref; + String[] parts = ref.split("\\/"); + if (parts.length >= 2) { + String t = parts[0]; + String i = parts[1]; for (Element entry : entries) { - if (entry.hasDescendant(element)) { + String fu = entry.getNamedChildValue("fullUrl"); + if (fu != null && fu.equals(u)) + return entry; + if (u == null) { + Element resource = entry.getNamedChild("resource"); + if (resource != null) { + String et = resource.getType(); + String eid = resource.getNamedChildValue("id"); + if (t.equals(et) && i.equals(eid)) return entry; } + } } - return null; + } + return null; } + } - private ResolvedReference makeExternalRef(Element external, String path) { - ResolvedReference res = new ResolvedReference(); - res.setResource(external); - res.setFocus(external); - res.setExternal(true); - res.setStack(new NodeStack(external, path)); - return res; + private ElementDefinition resolveNameReference(StructureDefinitionSnapshotComponent snapshot, String contentReference) { + for (ElementDefinition ed : snapshot.getElement()) + if (contentReference.equals("#" + ed.getId())) + return ed; + return null; + } + + private StructureDefinition resolveProfile(StructureDefinition profile, String pr) { + if (pr.startsWith("#")) { + for (Resource r : profile.getContained()) { + if (r.getId().equals(pr.substring(1)) && r instanceof StructureDefinition) + return (StructureDefinition) r; + } + return null; + } else { + long t = System.nanoTime(); + StructureDefinition fr = context.fetchResource(StructureDefinition.class, pr); + sdTime = sdTime + (System.nanoTime() - t); + return fr; } + } - - private Element resolve(Object appContext, String ref, NodeStack stack, List errors, String path) throws IOException, FHIRException { - Element local = localResolve(ref, stack, errors, path, null, null).getFocus(); - if (local != null) - return local; - if (fetcher == null) - return null; - if (fetchCache.containsKey(ref)) { - return fetchCache.get(ref); - } else { - Element res = fetcher.fetch(appContext, ref); - fetchCache.put(ref, res); - return res; - } + private ElementDefinition resolveType(String type, List list) { + for (TypeRefComponent tr : list) { + String url = tr.getWorkingCode(); + if (!Utilities.isAbsoluteUrl(url)) + url = "http://hl7.org/fhir/StructureDefinition/" + url; + long t = System.nanoTime(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + sdTime = sdTime + (System.nanoTime() - t); + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + return sd.getSnapshot().getElement().get(0); } + return null; + } - private ValueSet resolveBindingReference(DomainResource ctxt, String reference, String uri) { - if (reference != null) { - if (reference.startsWith("#")) { - for (Resource c : ctxt.getContained()) { - if (c.getId().equals(reference.substring(1)) && (c instanceof ValueSet)) - return (ValueSet) c; - } - return null; - } else { - long t = System.nanoTime(); - ValueSet fr = context.fetchResource(ValueSet.class, reference); - if (fr == null) { - if (!Utilities.isAbsoluteUrl(reference)) { - reference = resolve(uri, reference); - fr = context.fetchResource(ValueSet.class, reference); - } - } - if (fr == null) - fr = ValueSetUtilities.generateImplicitValueSet(reference); - txTime = txTime + (System.nanoTime() - t); - return fr; - } - } else - return null; - } + public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) { + this.anyExtensionsAllowed = anyExtensionsAllowed; + } - private String resolve(String uri, String ref) { - if (isBlank(uri)) { - return ref; - } - String[] up = uri.split("\\/"); - String[] rp = ref.split("\\/"); - if (context.getResourceNames().contains(up[up.length - 2]) && context.getResourceNames().contains(rp[0])) { - StringBuilder b = new StringBuilder(); - for (int i = 0; i < up.length - 2; i++) { - b.append(up[i]); - b.append("/"); - } - b.append(ref); - return b.toString(); - } else - return ref; - } + public IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value) { + bpWarnings = value; + return this; + } - private Element resolveInBundle(List entries, String ref, String fullUrl, String type, String id) { - if (Utilities.isAbsoluteUrl(ref)) { - // if the reference is absolute, then you resolve by fullUrl. No other thinking is required. - for (Element entry : entries) { - String fu = entry.getNamedChildValue("fullUrl"); - if (ref.equals(fu)) - return entry; - } - return null; - } else { - // split into base, type, and id - String u = null; - if (fullUrl != null && fullUrl.endsWith(type + "/" + id)) - // fullUrl = complex - u = fullUrl.substring(0, fullUrl.length() - (type + "/" + id).length()) + ref; -// u = fullUrl.substring((type+"/"+id).length())+ref; - String[] parts = ref.split("\\/"); - if (parts.length >= 2) { - String t = parts[0]; - String i = parts[1]; - for (Element entry : entries) { - String fu = entry.getNamedChildValue("fullUrl"); - if (fu != null && fu.equals(u)) - return entry; - if (u == null) { - Element resource = entry.getNamedChild("resource"); - if (resource != null) { - String et = resource.getType(); - String eid = resource.getNamedChildValue("id"); - if (t.equals(et) && i.equals(eid)) - return entry; - } - } - } - } - return null; - } - } + @Override + public void setCheckDisplay(CheckDisplayOption checkDisplay) { + this.checkDisplay = checkDisplay; + } - private ElementDefinition resolveNameReference(StructureDefinitionSnapshotComponent snapshot, String contentReference) { - for (ElementDefinition ed : snapshot.getElement()) - if (contentReference.equals("#" + ed.getId())) - return ed; - return null; - } + public void setSuppressLoincSnomedMessages(boolean suppressLoincSnomedMessages) { + this.suppressLoincSnomedMessages = suppressLoincSnomedMessages; + } - private StructureDefinition resolveProfile(StructureDefinition profile, String pr) { - if (pr.startsWith("#")) { - for (Resource r : profile.getContained()) { - if (r.getId().equals(pr.substring(1)) && r instanceof StructureDefinition) - return (StructureDefinition) r; - } - return null; - } else { - long t = System.nanoTime(); - StructureDefinition fr = context.fetchResource(StructureDefinition.class, pr); - sdTime = sdTime + (System.nanoTime() - t); - return fr; - } - } + public IdStatus getResourceIdRule() { + return resourceIdRule; + } - private ElementDefinition resolveType(String type, List list) { - for (TypeRefComponent tr : list) { - String url = tr.getWorkingCode(); - if (!Utilities.isAbsoluteUrl(url)) - url = "http://hl7.org/fhir/StructureDefinition/" + url; - long t = System.nanoTime(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - sdTime = sdTime + (System.nanoTime() - t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) - return sd.getSnapshot().getElement().get(0); - } - return null; - } - - public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) { - this.anyExtensionsAllowed = anyExtensionsAllowed; - } - - public IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value) { - bpWarnings = value; - return this; - } - - @Override - public void setCheckDisplay(CheckDisplayOption checkDisplay) { - this.checkDisplay = checkDisplay; - } - - public void setSuppressLoincSnomedMessages(boolean suppressLoincSnomedMessages) { - this.suppressLoincSnomedMessages = suppressLoincSnomedMessages; - } - - public IdStatus getResourceIdRule() { - return resourceIdRule; - } - - public void setResourceIdRule(IdStatus resourceIdRule) { - this.resourceIdRule = resourceIdRule; - } + public void setResourceIdRule(IdStatus resourceIdRule) { + this.resourceIdRule = resourceIdRule; + } - public boolean isAllowXsiLocation() { - return allowXsiLocation; - } + public boolean isAllowXsiLocation() { + return allowXsiLocation; + } - public void setAllowXsiLocation(boolean allowXsiLocation) { - this.allowXsiLocation = allowXsiLocation; - } + public void setAllowXsiLocation(boolean allowXsiLocation) { + this.allowXsiLocation = allowXsiLocation; + } - /** - * @param element - the candidate that might be in the slice - * @param path - for reporting any errors. the XPath for the element - * @param slicer - the definition of how slicing is determined - * @param ed - the slice for which to test membership - * @param errors - * @param stack - * @return - * @throws DefinitionException - * @throws DefinitionException - * @throws IOException - * @throws FHIRException - */ - private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List errors, List sliceInfo, NodeStack stack) throws DefinitionException, FHIRException { - if (!slicer.getSlicing().hasDiscriminator()) - return false; // cannot validate in this case + /** + * @param element - the candidate that might be in the slice + * @param path - for reporting any errors. the XPath for the element + * @param slicer - the definition of how slicing is determined + * @param ed - the slice for which to test membership + * @param errors + * @param stack + * @return + * @throws DefinitionException + * @throws DefinitionException + * @throws IOException + * @throws FHIRException + */ + private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List errors, List sliceInfo, NodeStack stack) throws DefinitionException, FHIRException { + if (!slicer.getSlicing().hasDiscriminator()) + return false; // cannot validate in this case - ExpressionNode n = (ExpressionNode) ed.getUserData("slice.expression.cache"); - if (n == null) { - long t = System.nanoTime(); - // GG: this approach is flawed because it treats discriminators individually rather than collectively - StringBuilder expression = new StringBuilder("true"); - boolean anyFound = false; - Set discriminators = new HashSet<>(); - for (ElementDefinitionSlicingDiscriminatorComponent s : slicer.getSlicing().getDiscriminator()) { - String discriminator = s.getPath(); - discriminators.add(discriminator); + ExpressionNode n = (ExpressionNode) ed.getUserData("slice.expression.cache"); + if (n == null) { + long t = System.nanoTime(); + // GG: this approach is flawed because it treats discriminators individually rather than collectively + StringBuilder expression = new StringBuilder("true"); + boolean anyFound = false; + Set discriminators = new HashSet<>(); + for (ElementDefinitionSlicingDiscriminatorComponent s : slicer.getSlicing().getDiscriminator()) { + String discriminator = s.getPath(); + discriminators.add(discriminator); - List criteriaElements = getCriteriaForDiscriminator(path, ed, discriminator, profile, s.getType() == DiscriminatorType.PROFILE); - boolean found = false; - for (ElementDefinition criteriaElement : criteriaElements) { - found = true; - if (s.getType() == DiscriminatorType.TYPE) { - String type = null; - if (!criteriaElement.getPath().contains("[") && discriminator.contains("[")) { - discriminator = discriminator.substring(0, discriminator.indexOf('[')); - String lastNode = tail(discriminator); - type = tail(criteriaElement.getPath()).substring(lastNode.length()); - type = type.substring(0, 1).toLowerCase() + type.substring(1); - } else if (!criteriaElement.hasType() || criteriaElement.getType().size() == 1) { - if (discriminator.contains("[")) - discriminator = discriminator.substring(0, discriminator.indexOf('[')); - type = criteriaElement.getType().get(0).getWorkingCode(); - } else if (criteriaElement.getType().size() > 1) { - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); - } else - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); - if (discriminator.isEmpty()) - expression.append(" and $this is " + type); - else - expression.append(" and " + discriminator + " is " + type); - } else if (s.getType() == DiscriminatorType.PROFILE) { - if (criteriaElement.getType().size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } - if (criteriaElement.getType().size() != 1) { - throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } - List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); - if (list.size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } else if (list.size() > 1) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); - for (CanonicalType c : list) { - b.append(discriminator + ".conformsTo('" + c.getValue() + "')"); - } - expression.append(" and (" + b + ")"); - } else { - expression.append(" and " + discriminator + ".conformsTo('" + list.get(0).getValue() + "')"); - } - } else if (s.getType() == DiscriminatorType.EXISTS) { - if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1) - expression.append(" and (" + discriminator + ".exists())"); - else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) - expression.append(" and (" + discriminator + ".exists().not())"); - else - throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); - } else if (criteriaElement.hasFixed()) { - buildFixedExpression(ed, expression, discriminator, criteriaElement); - } else if (criteriaElement.hasPattern()) { - buildPattternExpression(ed, expression, discriminator, criteriaElement); - } else if (criteriaElement.hasBinding() && criteriaElement.getBinding().hasStrength() && criteriaElement.getBinding().getStrength().equals(BindingStrength.REQUIRED) && criteriaElement.getBinding().hasValueSet()) { - expression.append(" and (" + discriminator + " memberOf '" + criteriaElement.getBinding().getValueSet() + "')"); - } else { - found = false; - } - if (found) - break; - } - if (found) - anyFound = true; - } - if (!anyFound) { - if (slicer.getSlicing().getDiscriminator().size() > 1) - throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); - else - throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); - } - - try { - n = fpe.parse(fixExpr(expression.toString())); - } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); - } - fpeTime = fpeTime + (System.nanoTime() - t); - ed.setUserData("slice.expression.cache", n); - } - - ValidatorHostContext shc = hostContext.forSlicing(); - boolean pass = evaluateSlicingExpression(shc, element, path, profile, n); - if (!pass) { - slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Does not match slice'" + ed.getSliceName(), "discriminator = " + Utilities.escapeXml(n.toString())); - for (String url : shc.getSliceRecords().keySet()) { - slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + stack.getLiteralPath() + " against profile " + url, - "Profile " + url + " does not match for " + stack.getLiteralPath() + " because of the following profile issues: " + errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))); - } - } - return pass; - } - - public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException { - String msg; - boolean ok; - try { - long t = System.nanoTime(); - ok = fpe.evaluateToBoolean(hostContext.forProfile(profile), hostContext.getResource(), hostContext.getRootResource(), element, n); - fpeTime = fpeTime + (System.nanoTime() - t); - msg = fpe.forLog(); - } catch (Exception ex) { - ex.printStackTrace(); - throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); - } - return ok; - } - - private void buildPattternExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { - DataType pattern = criteriaElement.getPattern(); - if (pattern instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) pattern; - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, discriminator, cc); - } else if (pattern instanceof Identifier) { - Identifier ii = (Identifier) pattern; - expression.append(" and "); - buildIdentifierExpression(ed, expression, discriminator, ii); - } else - throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); - } - - private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) - throws DefinitionException { - if (ii.hasExtension()) - throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - boolean first = true; - expression.append(discriminator + ".where("); - if (ii.hasSystem()) { - first = false; - expression.append("system = '" + ii.getSystem() + "'"); - } - if (ii.hasValue()) { - if (first) - first = false; - else - expression.append(" and "); - expression.append("value = '" + ii.getValue() + "'"); - } - if (ii.hasUse()) { - if (first) - first = false; - else - expression.append(" and "); - expression.append("use = '" + ii.getUse() + "'"); - } - if (ii.hasType()) { - if (first) - first = false; - else - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, "type", ii.getType()); - } - expression.append(").exists()"); - } - - private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) - throws DefinitionException { - if (cc.hasText()) - throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (!cc.hasCoding()) - throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (cc.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - boolean firstCoding = true; - for (Coding c : cc.getCoding()) { - if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (firstCoding) firstCoding = false; - else expression.append(" and "); - expression.append(discriminator + ".coding.where("); - boolean first = true; - if (c.hasSystem()) { - first = false; - expression.append("system = '" + c.getSystem() + "'"); - } - if (c.hasVersion()) { - if (first) first = false; - else expression.append(" and "); - expression.append("version = '" + c.getVersion() + "'"); - } - if (c.hasCode()) { - if (first) first = false; - else expression.append(" and "); - expression.append("code = '" + c.getCode() + "'"); - } - if (c.hasDisplay()) { - if (first) first = false; - else expression.append(" and "); - expression.append("display = '" + c.getDisplay() + "'"); - } - expression.append(").exists()"); - } - } - - private void buildFixedExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { - DataType fixed = criteriaElement.getFixed(); - if (fixed instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) fixed; - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, discriminator, cc); - } else if (fixed instanceof Identifier) { - Identifier ii = (Identifier) fixed; - expression.append(" and "); - buildIdentifierExpression(ed, expression, discriminator, ii); - } else { - expression.append(" and ("); - if (fixed instanceof StringType) { - Gson gson = new Gson(); - String json = gson.toJson((StringType) fixed); - String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); - expression.append("'" + escapedString + "'"); - } else if (fixed instanceof UriType) { - expression.append("'" + ((UriType) fixed).asStringValue() + "'"); - } else if (fixed instanceof IntegerType) { - expression.append(((IntegerType) fixed).asStringValue()); - } else if (fixed instanceof DecimalType) { - expression.append(((IntegerType) fixed).asStringValue()); - } else if (fixed instanceof BooleanType) { - expression.append(((BooleanType) fixed).asStringValue()); + List criteriaElements = getCriteriaForDiscriminator(path, ed, discriminator, profile, s.getType() == DiscriminatorType.PROFILE); + boolean found = false; + for (ElementDefinition criteriaElement : criteriaElements) { + found = true; + if (s.getType() == DiscriminatorType.TYPE) { + String type = null; + if (!criteriaElement.getPath().contains("[") && discriminator.contains("[")) { + discriminator = discriminator.substring(0, discriminator.indexOf('[')); + String lastNode = tail(discriminator); + type = tail(criteriaElement.getPath()).substring(lastNode.length()); + type = type.substring(0, 1).toLowerCase() + type.substring(1); + } else if (!criteriaElement.hasType() || criteriaElement.getType().size() == 1) { + if (discriminator.contains("[")) + discriminator = discriminator.substring(0, discriminator.indexOf('[')); + type = criteriaElement.getType().get(0).getWorkingCode(); + } else if (criteriaElement.getType().size() > 1) { + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); } else - throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); - expression.append(" in " + discriminator + ")"); - } - } - - // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) - private void start(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { - checkLang(resource, stack); - - if ("Bundle".equals(element.fhirType())) { - resolveBundleReferences(element, new ArrayList()); - } - startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials()); - - List res = new ArrayList<>(); - Element meta = element.getNamedChild("meta"); - if (meta != null) { - List profiles = new ArrayList(); - meta.getNamedChildren("profile", profiles); - int i = 0; - for (Element profile : profiles) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); - if (!defn.getUrl().equals(profile.primitiveValue())) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, "Profile reference '" + profile.primitiveValue() + "' could not be resolved, so has not been checked")) { - startInner(hostContext, errors, resource, element, sd, stack, false); - } - } - i++; - } - } - } - - private void resolveBundleReferences(Element element, List bundles) { - if (!element.hasUserData("validator.bundle.resolved")) { - element.setUserData("validator.bundle.resolved", true); - List list = new ArrayList(); - list.addAll(bundles); - list.add(0, element); - List entries = element.getChildrenByName("entry"); - for (Element entry : entries) { - String fu = entry.getChildValue("fullUrl"); - Element r = entry.getNamedChild("resource"); - if (r != null) { - resolveBundleReferencesInResource(list, r, fu); - } - } - } - } - - private void resolveBundleReferencesInResource(List bundles, Element r, String fu) { - r.setUserData("validator.bundle.resolution-resource", null); - if ("Bundle".equals(r.fhirType())) { - resolveBundleReferences(r, bundles); - } else { - for (Element child : r.getChildren()) { - resolveBundleReferencesForElement(bundles, r, fu, child); - } - } - } - - private void resolveBundleReferencesForElement(List bundles, Element resource, String fu, Element element) { - if ("Reference".equals(element.fhirType())) { - String ref = element.getChildValue("reference"); - if (!Utilities.noString(ref)) { - for (Element bundle : bundles) { - List entries = bundle.getChildren("entry"); - Element tgt = resolveInBundle(entries, ref, fu, resource.fhirType(), resource.getIdBase()); - if (tgt != null) { - element.setUserData("validator.bundle.resolution", tgt.getNamedChild("resource")); - return; - } - } - element.setUserData("validator.bundle.resolution-failed", ref); - } - } else { - element.setUserData("validator.bundle.resolution-noref", null); - for (Element child : element.getChildren()) { - resolveBundleReferencesForElement(bundles, resource, fu, child); - } - } - - } - - public void startInner(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials) { - // the first piece of business is to see if we've validated this resource against this profile before. - // if we have (*or if we still are*), then we'll just return our existing errors - ResourceValidationTracker resTracker = getResourceTracker(element); - List cachedErrors = resTracker.getOutcomes(defn); - if (cachedErrors != null) { - for (ValidationMessage vm : cachedErrors) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - return; - } - if (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")) { - List localErrors = new ArrayList(); - resTracker.startValidating(defn); - trackUsage(defn, hostContext, element); - validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null); - resTracker.storeOutcomes(defn, localErrors); - for (ValidationMessage vm : localErrors) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - if (checkSpecials) { - checkSpecials(hostContext, errors, element, stack, checkSpecials); - validateResourceRules(errors, element, stack); - } - } - - public void checkSpecials(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack, boolean checkSpecials) { - // specific known special validations - if (element.getType().equals("Bundle")) { - validateBundle(errors, element, stack, checkSpecials); - } else if (element.getType().equals("Observation")) { - validateObservation(errors, element, stack); - } else if (element.getType().equals("Questionnaire")) { - ArrayList parents = new ArrayList<>(); - parents.add(element); - validateQuestionannaireItem(errors, element, element, stack, parents); - } else if (element.getType().equals("QuestionnaireResponse")) { - validateQuestionannaireResponse(hostContext, errors, element, stack); - } else if (element.getType().equals("CapabilityStatement")) { - validateCapabilityStatement(errors, element, stack); - } else if (element.getType().equals("CodeSystem")) { - validateCodeSystem(errors, element, stack); - } - } - - private ResourceValidationTracker getResourceTracker(Element element) { - ResourceValidationTracker res = resourceTracker.get(element); - if (res == null) { - res = new ResourceValidationTracker(); - resourceTracker.put(element, res); - } - return res; - } - - private void validateQuestionannaireItem(List errors, Element element, Element questionnaire, NodeStack stack, List parents) { - List list = getItems(element); - for (int i = 0; i < list.size(); i++) { - Element e = list.get(i); - NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - validateQuestionnaireElement(errors, ns, questionnaire, e, parents); - List np = new ArrayList(); - np.add(e); - np.addAll(parents); - validateQuestionannaireItem(errors, e, questionnaire, ns, np); - } - } - - private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List parents) { - // R4+ - if ((FHIRVersion.isR4Plus(context.getVersion())) && (item.hasChildren("enableWhen"))) { - List ewl = item.getChildren("enableWhen"); - for (Element ew : ewl) { - String ql = ew.getNamedChildValue("question"); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) { - Element tgt = getQuestionById(item, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) { - tgt = getQuestionById(questionnaire, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find target '" + ql + "' for this question enableWhen")) { - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) { - if (!isBefore(item, tgt, parents)) { - warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, "The target of this enableWhen rule (" + ql + ") comes after the question itself"); - } - } - } - } - } - } - } - } - - private boolean isBefore(Element item, Element tgt, List parents) { - // we work up the list, looking for tgt in the children of the parents - if (parents.contains(tgt)) { - // actually, if the target is a parent, that's automatically ok - return true; - } - for (Element p : parents) { - int i = findIndex(p, item); - int t = findIndex(p, tgt); - if (i > -1 && t > -1) { - return i > t; - } - } - return false; // unsure... shouldn't ever get to this point; - } - - - private int findIndex(Element parent, Element descendant) { - for (int i = 0; i < parent.getChildren().size(); i++) { - if (parent.getChildren().get(i) == descendant || isChild(parent.getChildren().get(i), descendant)) - return i; - } - return -1; - } - - private boolean isChild(Element element, Element descendant) { - for (Element e : element.getChildren()) { - if (e == descendant) - return true; - if (isChild(e, descendant)) - return true; - } - return false; - } - - private Element getQuestionById(Element focus, String ql) { - List list = getItems(focus); - for (Element item : list) { - String v = item.getNamedChildValue("linkId"); - if (ql.equals(v)) - return item; - Element tgt = getQuestionById(item, ql); - if (tgt != null) - return tgt; - } - return null; - - } - - private List getItems(Element element) { - List list = new ArrayList<>(); - element.getNamedChildren("item", list); - return list; - } - - private void checkLang(Element resource, NodeStack stack) { - String lang = resource.getNamedChildValue("language"); - if (!Utilities.noString(lang)) - stack.workingLang = lang; - } - - private void validateResourceRules(List errors, Element element, NodeStack stack) { - String lang = element.getNamedChildValue("language"); - Element text = element.getNamedChild("text"); - if (text != null) { - Element div = text.getNamedChild("div"); - if (lang != null && div != null) { - XhtmlNode xhtml = div.getXhtml(); - String l = xhtml.getAttribute("lang"); - String xl = xhtml.getAttribute("xml:lang"); - if (l == null && xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else { - if (l == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else if (!l.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has a lang (" + l + "), but they differ "); - } - if (xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else if (!xl.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has an xml:lang (" + xl + "), but they differ "); - } - } - } - } - // security tags are a set (system|code) - Element meta = element.getNamedChild("meta"); - if (meta != null) { - Set tags = new HashSet<>(); - List list = new ArrayList<>(); - meta.getNamedChildren("security", list); - int i = 0; - for (Element e : list) { - String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); - rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s), "Duplicate Security Label " + s); - tags.add(s); - i++; - } - } - } - - private void validateCapabilityStatement(List errors, Element cs, NodeStack stack) { - int iRest = 0; - for (Element rest : cs.getChildrenByName("rest")) { - int iResource = 0; - for (Element resource : rest.getChildrenByName("resource")) { - int iSP = 0; - for (Element searchParam : resource.getChildrenByName("searchParam")) { - String ref = searchParam.getChildValue("definition"); - String type = searchParam.getChildValue("type"); - if (!Utilities.noString(ref)) { - SearchParameter sp = context.fetchResource(SearchParameter.class, ref); - if (sp != null) { - rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", - sp.getType().toCode().equals(type), "Type mismatch - SearchParameter '" + sp.getUrl() + "' type is " + sp.getType().toCode() + ", but type here is " + type); - } - } - iSP++; - } - iResource++; - } - iRest++; - } - } - - private void validateCodeSystem(List errors, Element cs, NodeStack stack) { - String url = cs.getNamedChildValue("url"); - String vsu = cs.getNamedChildValue("valueSet"); - if (!Utilities.noString(vsu)) { - ValueSet vs; - try { - vs = context.fetchResourceWithException(ValueSet.class, vsu); - } catch (FHIRException e) { - vs = null; - } - if (vs != null) { - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but it is an expansion")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a single include")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a matching system (" + vs.getCompose().getInclude().get(0).getSystem() + ")")) { - rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() - && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but the include has extra details"); - } - } - } // todo... try getting the value set the other way... - } - - private void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { - Element q = element.getNamedChild("questionnaire"); - String questionnaire = null; - if (q != null) { - /* - * q.getValue() is correct for R4 content, but we'll also accept the second - * option just in case we're validating raw STU3 content. Being lenient here - * isn't the end of the world since if someone is actually doing the reference - * wrong in R4 content it'll get flagged elsewhere by the validator too - */ - if (isNotBlank(q.getValue())) { - questionnaire = q.getValue(); - } else if (isNotBlank(q.getChildValue("reference"))) { - questionnaire = q.getChildValue("reference"); - } - } - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { - long t = System.nanoTime(); - Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); - sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire '" + questionnaire + "' could not be resolved, so no validation can be performed against the base questionnaire")) { - boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); - validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); - } - } - } - - private Questionnaire loadQuestionnaire(Element resource, String id) throws FHIRException { - try { - for (Element contained : resource.getChildren("contained")) { - if (contained.getIdBase().equals(id)) { - FhirPublication v = FhirPublication.fromCode(context.getVersion()); - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id); - byte[] json = bs.toByteArray(); - switch (v) { - case DSTU1: - throw new FHIRException("Unsupported version R1"); - case DSTU2: - org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); - Resource r5 = VersionConvertor_10_50.convertResource(r2); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case DSTU2016May: - org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); - r5 = VersionConvertor_14_50.convertResource(r2a); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case STU3: - org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); - r5 = VersionConvertor_30_50.convertResource(r3, false); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R4: - org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); - r5 = VersionConvertor_40_50.convertResource(r4); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R5: - r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - } - } - } - return null; - } catch (IOException e) { - throw new FHIRException(e); - } - } - - private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - String text = element.getNamedChildValue("text"); - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId " + qItem.getLinkId()); - - List answers = new ArrayList(); - element.getNamedChildren("answer", answers); - if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); - else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); - } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled - // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? - // GDG 2019-07-13 -// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers), "Item has answer (2), even though it is not enabled "+qItem.getLinkId()); - } - - if (answers.size() > 1) - rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); - - for (Element answer : answers) { - NodeStack ns = stack.push(answer, -1, null, null); - if (qItem.getType() != null) { - switch (qItem.getType()) { - case GROUP: - rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); - break; - case DISPLAY: // nothing - break; - case BOOLEAN: - validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); - break; - case DECIMAL: - validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); - break; - case INTEGER: - validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); - break; - case DATE: - validateQuestionnaireResponseItemType(errors, answer, ns, "date"); - break; - case DATETIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); - break; - case TIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "time"); - break; - case STRING: - validateQuestionnaireResponseItemType(errors, answer, ns, "string"); - break; - case TEXT: - validateQuestionnaireResponseItemType(errors, answer, ns, "text"); - break; - case URL: - validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); - break; - case ATTACHMENT: - validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); - break; - case REFERENCE: - validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); - break; - case QUANTITY: - if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) - if (qItem.hasExtension("???")) - validateQuestionnaireResponseItemQuantity(errors, answer, ns); - break; - case CHOICE: - String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) - checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); - } - break; - case OPENCHOICE: - itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) - checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) - checkOption(errors, answer, ns, qsrc, qItem, "string", true); - } - break; -// case QUESTION: - case NULL: - // no validation - break; - } - } - validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); - } - if (qItem.getType() == null) { - fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item " + qItem.getLinkId() + " does not contain a type"); - } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { - List items = new ArrayList(); - element.getNamedChildren("item", items); - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); - } else { - validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); - } - } - - private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { - return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; - } - - private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - if (elements.size() > 1) - rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); - int i = 0; - for (Element element : elements) { - NodeStack ns = stack.push(element, i, null, null); - validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); - i++; - } - } - - private int getLinkIdIndex(List qItems, String linkId) { - for (int i = 0; i < qItems.size(); i++) { - if (linkId.equals(qItems.get(i).getLinkId())) - return i; - } - return -1; - } - - private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - List items = new ArrayList(); - element.getNamedChildren("item", items); - // now, sort into stacks - Map> map = new HashMap>(); - int lastIndex = -1; - for (Element item : items) { - String linkId = item.getNamedChildValue("linkId"); - if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), "No LinkId, so can't be validated")) { - int index = getLinkIdIndex(qItems, linkId); - if (index == -1) { - QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); - if (qItem != null) { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); - NodeStack ns = stack.push(item, -1, null, null); - validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); - } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId '" + linkId + "' not found in questionnaire"); - } else { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); - lastIndex = index; - - // If an item has a child called "linkId" but no child called "answer", - // we'll treat it as not existing for the purposes of enableWhen validation - if (item.hasChildren("answer") || item.hasChildren("item")) { - List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); - mapItem.add(item); - } - } - } - } - - // ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order - for (QuestionnaireItemComponent qItem : qItems) { - List mapItem = map.get(qItem.getLinkId()); - validateQuestionnaireResponseItem(hostContext, qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); - } - } - - public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { - boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe); - if (mapItem != null) { - if (!enabled) { - int i = 0; - for (Element e : mapItem) { - NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, "Item has answer, even though it is not enabled (item id = '" + qItem.getLinkId() + "')"); - i++; - } - } - - // Recursively validate child items - validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); - - } else { - - // item is missing, is the question enabled? - if (enabled && qItem.getRequired()) { - String message = "No response found for required item with id = '" + qItem.getLinkId() + "'"; - if (inProgress) { - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); - } else { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); - } - } - - } - - } - - private String misplacedItemError(QuestionnaireItemComponent qItem) { - return qItem.hasLinkId() ? String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId()) : "Structural Error: item is in the wrong place"; - } - - private void validateQuestionnaireResponseItemQuantity(List errors, Element answer, NodeStack stack) { - - } - - private String validateQuestionnaireResponseItemType(List errors, Element element, NodeStack stack, String... types) { - List values = new ArrayList(); - element.getNamedChildrenWithWildcard("value[x]", values); - for (int i = 0; i < types.length; i++) { - if (types[i].equals("text")) { - types[i] = "string"; - } - } - if (values.size() > 0) { - NodeStack ns = stack.push(values.get(0), -1, null, null); - CommaSeparatedStringBuilder l = new CommaSeparatedStringBuilder(); - for (String s : types) { - l.append(s); - if (values.get(0).getName().equals("value" + Utilities.capitalize(s))) - return (s); - } - if (types.length == 1) - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be of type " + types[0]); + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); + if (discriminator.isEmpty()) + expression.append(" and $this is " + type); else - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be one of the types " + l.toString()); - } - return null; - } - - private QuestionnaireItemComponent findQuestionnaireItem(Questionnaire qSrc, String linkId) { - return findItem(qSrc.getItem(), linkId); - } - - private QuestionnaireItemComponent findItem(List list, String linkId) { - for (QuestionnaireItemComponent item : list) { - if (linkId.equals(item.getLinkId())) - return item; - QuestionnaireItemComponent result = findItem(item.getItem(), linkId); - if (result != null) - return result; - } - return null; - } - - private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { - ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); - if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, "ValueSet " + describeReference(ref) + " not found by validator")) { - try { - Coding c = ObjectConverter.readAsCoding(value); - if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { - if (theOpenChoice) { - return; - } - } - - long t = System.nanoTime(); - ValidationResult res = context.validateCode(new ValidationOptions(stack.workingLang), c, vs); - txTime = txTime + (System.nanoTime() - t); - if (!res.isOk()) { - txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire"); - } else if (res.getSeverity() != null) { - super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options"); + expression.append(" and " + discriminator + " is " + type); + } else if (s.getType() == DiscriminatorType.PROFILE) { + if (criteriaElement.getType().size() == 0) { + throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); } + if (criteriaElement.getType().size() != 1) { + throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + } + List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); + if (list.size() == 0) { + throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + } else if (list.size() > 1) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); + for (CanonicalType c : list) { + b.append(discriminator + ".conformsTo('" + c.getValue() + "')"); + } + expression.append(" and (" + b + ")"); + } else { + expression.append(" and " + discriminator + ".conformsTo('" + list.get(0).getValue() + "')"); + } + } else if (s.getType() == DiscriminatorType.EXISTS) { + if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1) + expression.append(" and (" + discriminator + ".exists())"); + else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) + expression.append(" and (" + discriminator + ".exists().not())"); + else + throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); + } else if (criteriaElement.hasFixed()) { + buildFixedExpression(ed, expression, discriminator, criteriaElement); + } else if (criteriaElement.hasPattern()) { + buildPattternExpression(ed, expression, discriminator, criteriaElement); + } else if (criteriaElement.hasBinding() && criteriaElement.getBinding().hasStrength() && criteriaElement.getBinding().getStrength().equals(BindingStrength.REQUIRED) && criteriaElement.getBinding().hasValueSet()) { + expression.append(" and (" + discriminator + " memberOf '" + criteriaElement.getBinding().getValueSet() + "')"); + } else { + found = false; + } + if (found) + break; } - } - - private void validateAnswerCode(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) { - Element v = answer.getNamedChild("valueCoding"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) - checkCodingOption(errors, answer, stack, qSrc, qItem, theOpenChoice); - // validateAnswerCode(errors, v, stack, qItem.getOption()); - else if (qItem.hasAnswerValueSet()) - validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); + if (found) + anyFound = true; + } + if (!anyFound) { + if (slicer.getSlicing().getDiscriminator().size() > 1) + throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate options because no option or options are provided"); + throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); + } + + try { + n = fpe.parse(fixExpr(expression.toString())); + } catch (FHIRLexerException e) { + throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + } + fpeTime = fpeTime + (System.nanoTime() - t); + ed.setUserData("slice.expression.cache", n); } - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { - checkOption(errors, answer, stack, qSrc, qItem, type, false); + ValidatorHostContext shc = hostContext.forSlicing(); + boolean pass = evaluateSlicingExpression(shc, element, path, profile, n); + if (!pass) { + slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Does not match slice'" + ed.getSliceName(), "discriminator = " + Utilities.escapeXml(n.toString())); + for (String url : shc.getSliceRecords().keySet()) { + slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + stack.getLiteralPath() + " against profile " + url, + "Profile " + url + " does not match for " + stack.getLiteralPath() + " because of the following profile issues: " + errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))); + } } + return pass; + } - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { - if (type.equals("integer")) checkIntegerOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("date")) checkDateOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("time")) checkTimeOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("string")) checkStringOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("Coding")) checkCodingOption(errors, answer, stack, qSrc, qItem, openChoice); + public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException { + String msg; + boolean ok; + try { + long t = System.nanoTime(); + ok = fpe.evaluateToBoolean(hostContext.forProfile(profile), hostContext.getResource(), hostContext.getRootResource(), element, n); + fpeTime = fpeTime + (System.nanoTime() - t); + msg = fpe.forLog(); + } catch (Exception ex) { + ex.printStackTrace(); + throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); } + return ok; + } - private void checkIntegerOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueInteger"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueIntegerType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type integer"); - } else { - boolean found = false; - for (IntegerType item : list) { - if (item.getValue() == Integer.parseInt(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The integer " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate integer answer option because no option list is provided"); + private void buildPattternExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { + DataType pattern = criteriaElement.getPattern(); + if (pattern instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) pattern; + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, discriminator, cc); + } else if (pattern instanceof Identifier) { + Identifier ii = (Identifier) pattern; + expression.append(" and "); + buildIdentifierExpression(ed, expression, discriminator, ii); + } else + throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); + } + + private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) + throws DefinitionException { + if (ii.hasExtension()) + throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + boolean first = true; + expression.append(discriminator + ".where("); + if (ii.hasSystem()) { + first = false; + expression.append("system = '" + ii.getSystem() + "'"); } - - private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueDate"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueDateType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type date"); - } else { - boolean found = false; - for (DateType item : list) { - if (item.getValue().equals(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The date " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate date answer option because no option list is provided"); + if (ii.hasValue()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("value = '" + ii.getValue() + "'"); } - - private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueTime"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueTimeType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type time"); - } else { - boolean found = false; - for (TimeType item : list) { - if (item.getValue().equals(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The time " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate time answer option because no option list is provided"); + if (ii.hasUse()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("use = '" + ii.getUse() + "'"); } + if (ii.hasType()) { + if (first) + first = false; + else + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, "type", ii.getType()); + } + expression.append(").exists()"); + } - private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueString"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - if (components.getValue() != null) { - list.add(components.getValueStringType()); - } - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (!openChoice) { - if (list.isEmpty()) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type string"); - } else { - boolean found = false; - for (StringType item : list) { - if (item.getValue().equals((v.primitiveValue()))) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The string " + v.primitiveValue() + " is not a valid option"); - } - } - } - } else { - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate string answer option because no option list is provided"); + private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) + throws DefinitionException { + if (cc.hasText()) + throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); + if (!cc.hasCoding()) + throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); + if (cc.hasExtension()) + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + boolean firstCoding = true; + for (Coding c : cc.getCoding()) { + if (c.hasExtension()) + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + if (firstCoding) firstCoding = false; + else expression.append(" and "); + expression.append(discriminator + ".coding.where("); + boolean first = true; + if (c.hasSystem()) { + first = false; + expression.append("system = '" + c.getSystem() + "'"); + } + if (c.hasVersion()) { + if (first) first = false; + else expression.append(" and "); + expression.append("version = '" + c.getVersion() + "'"); + } + if (c.hasCode()) { + if (first) first = false; + else expression.append(" and "); + expression.append("code = '" + c.getCode() + "'"); + } + if (c.hasDisplay()) { + if (first) first = false; + else expression.append(" and "); + expression.append("display = '" + c.getDisplay() + "'"); + } + expression.append(").exists()"); + } + } + + private void buildFixedExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { + DataType fixed = criteriaElement.getFixed(); + if (fixed instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) fixed; + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, discriminator, cc); + } else if (fixed instanceof Identifier) { + Identifier ii = (Identifier) fixed; + expression.append(" and "); + buildIdentifierExpression(ed, expression, discriminator, ii); + } else { + expression.append(" and ("); + if (fixed instanceof StringType) { + Gson gson = new Gson(); + String json = gson.toJson((StringType) fixed); + String escapedString = json.substring(json.indexOf(":") + 2); + escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); + expression.append("'" + escapedString + "'"); + } else if (fixed instanceof UriType) { + expression.append("'" + ((UriType) fixed).asStringValue() + "'"); + } else if (fixed instanceof IntegerType) { + expression.append(((IntegerType) fixed).asStringValue()); + } else if (fixed instanceof DecimalType) { + expression.append(((IntegerType) fixed).asStringValue()); + } else if (fixed instanceof BooleanType) { + expression.append(((BooleanType) fixed).asStringValue()); + } else + throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); + expression.append(" in " + discriminator + ")"); + } + } + + // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) + private void start(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { + checkLang(resource, stack); + + if ("Bundle".equals(element.fhirType())) { + resolveBundleReferences(element, new ArrayList()); + } + startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials()); + + List res = new ArrayList<>(); + Element meta = element.getNamedChild("meta"); + if (meta != null) { + List profiles = new ArrayList(); + meta.getNamedChildren("profile", profiles); + int i = 0; + for (Element profile : profiles) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); + if (!defn.getUrl().equals(profile.primitiveValue())) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null,messages.getString("Profile_reference__could_not_be_resolved_so_has_not_been_checked"), profile.primitiveValue())) { + startInner(hostContext, errors, resource, element, sd, stack, false); + } } + i++; + } } + } - private void checkCodingOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueCoding"); - String system = v.getNamedChildValue("system"); - String code = v.getNamedChildValue("code"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - if (components.getValue() != null) { - list.add(components.getValueCoding()); - } - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type coding"); - } else { - boolean found = false; - for (Coding item : list) { - if (ObjectUtil.equals(item.getSystem(), system) && ObjectUtil.equals(item.getCode(), code)) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The code " + system + "::" + code + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate Coding option because no option list is provided"); - } - - private String tail(String path) { - return path.substring(path.lastIndexOf(".") + 1); - } - - private String tryParse(String ref) { - String[] parts = ref.split("\\/"); - switch (parts.length) { - case 1: - return null; - case 2: - return checkResourceType(parts[0]); - default: - if (parts[parts.length - 2].equals("_history")) - return checkResourceType(parts[parts.length - 4]); - else - return checkResourceType(parts[parts.length - 2]); + private void resolveBundleReferences(Element element, List bundles) { + if (!element.hasUserData("validator.bundle.resolved")) { + element.setUserData("validator.bundle.resolved", true); + List list = new ArrayList(); + list.addAll(bundles); + list.add(0, element); + List entries = element.getChildrenByName("entry"); + for (Element entry : entries) { + String fu = entry.getChildValue("fullUrl"); + Element r = entry.getNamedChild("resource"); + if (r != null) { + resolveBundleReferencesInResource(list, r, fu); } + } + } + } + + private void resolveBundleReferencesInResource(List bundles, Element r, String fu) { + r.setUserData("validator.bundle.resolution-resource", null); + if ("Bundle".equals(r.fhirType())) { + resolveBundleReferences(r, bundles); + } else { + for (Element child : r.getChildren()) { + resolveBundleReferencesForElement(bundles, r, fu, child); + } + } + } + + private void resolveBundleReferencesForElement(List bundles, Element resource, String fu, Element element) { + if ("Reference".equals(element.fhirType())) { + String ref = element.getChildValue("reference"); + if (!Utilities.noString(ref)) { + for (Element bundle : bundles) { + List entries = bundle.getChildren("entry"); + Element tgt = resolveInBundle(entries, ref, fu, resource.fhirType(), resource.getIdBase()); + if (tgt != null) { + element.setUserData("validator.bundle.resolution", tgt.getNamedChild("resource")); + return; + } + } + element.setUserData("validator.bundle.resolution-failed", ref); + } + } else { + element.setUserData("validator.bundle.resolution-noref", null); + for (Element child : element.getChildren()) { + resolveBundleReferencesForElement(bundles, resource, fu, child); + } } - private boolean typesAreAllReference(List theType) { - for (TypeRefComponent typeRefComponent : theType) { - if (typeRefComponent.getCode().equals("Reference") == false) { - return false; - } + } + + public void startInner(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials) { + // the first piece of business is to see if we've validated this resource against this profile before. + // if we have (*or if we still are*), then we'll just return our existing errors + ResourceValidationTracker resTracker = getResourceTracker(element); + List cachedErrors = resTracker.getOutcomes(defn); + if (cachedErrors != null) { + for (ValidationMessage vm : cachedErrors) { + if (!errors.contains(vm)) { + errors.add(vm); } + } + return; + } + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(),messages.getString("StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided"))) { + List localErrors = new ArrayList(); + resTracker.startValidating(defn); + trackUsage(defn, hostContext, element); + validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null); + resTracker.storeOutcomes(defn, localErrors); + for (ValidationMessage vm : localErrors) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + if (checkSpecials) { + checkSpecials(hostContext, errors, element, stack, checkSpecials); + validateResourceRules(errors, element, stack); + } + } + + public void checkSpecials(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack, boolean checkSpecials) { + // specific known special validations + if (element.getType().equals("Bundle")) { + validateBundle(errors, element, stack, checkSpecials); + } else if (element.getType().equals("Observation")) { + validateObservation(errors, element, stack); + } else if (element.getType().equals("Questionnaire")) { + ArrayList parents = new ArrayList<>(); + parents.add(element); + validateQuestionannaireItem(errors, element, element, stack, parents); + } else if (element.getType().equals("QuestionnaireResponse")) { + validateQuestionannaireResponse(hostContext, errors, element, stack); + } else if (element.getType().equals("CapabilityStatement")) { + validateCapabilityStatement(errors, element, stack); + } else if (element.getType().equals("CodeSystem")) { + validateCodeSystem(errors, element, stack); + } + } + + private ResourceValidationTracker getResourceTracker(Element element) { + ResourceValidationTracker res = resourceTracker.get(element); + if (res == null) { + res = new ResourceValidationTracker(); + resourceTracker.put(element, res); + } + return res; + } + + private void validateQuestionannaireItem(List errors, Element element, Element questionnaire, NodeStack stack, List parents) { + List list = getItems(element); + for (int i = 0; i < list.size(); i++) { + Element e = list.get(i); + NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); + validateQuestionnaireElement(errors, ns, questionnaire, e, parents); + List np = new ArrayList(); + np.add(e); + np.addAll(parents); + validateQuestionannaireItem(errors, e, questionnaire, ns, np); + } + } + + private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List parents) { + // R4+ + if ((FHIRVersion.isR4Plus(context.getVersion())) && (item.hasChildren("enableWhen"))) { + List ewl = item.getChildren("enableWhen"); + for (Element ew : ewl) { + String ql = ew.getNamedChildValue("question"); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null,messages.getString("Questions_with_an_enableWhen_must_have_a_value_for_the_question_link"))) { + Element tgt = getQuestionById(item, ql); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null,messages.getString("Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition"))) { + tgt = getQuestionById(questionnaire, ql); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null,messages.getString("Unable_to_find_target__for_this_question_enableWhen"), ql)) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item,messages.getString("Target_for_this_question_enableWhen_cant_reference_itself"))) { + if (!isBefore(item, tgt, parents)) { + warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false,messages.getString("The_target_of_this_enableWhen_rule__comes_after_the_question_itself"), ql); + } + } + } + } + } + } + } + } + + private boolean isBefore(Element item, Element tgt, List parents) { + // we work up the list, looking for tgt in the children of the parents + if (parents.contains(tgt)) { + // actually, if the target is a parent, that's automatically ok + return true; + } + for (Element p : parents) { + int i = findIndex(p, item); + int t = findIndex(p, tgt); + if (i > -1 && t > -1) { + return i > t; + } + } + return false; // unsure... shouldn't ever get to this point; + } + + + private int findIndex(Element parent, Element descendant) { + for (int i = 0; i < parent.getChildren().size(); i++) { + if (parent.getChildren().get(i) == descendant || isChild(parent.getChildren().get(i), descendant)) + return i; + } + return -1; + } + + private boolean isChild(Element element, Element descendant) { + for (Element e : element.getChildren()) { + if (e == descendant) + return true; + if (isChild(e, descendant)) return true; } + return false; + } - private void validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials) { - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); - String type = bundle.getNamedChildValue("type"); - type = StringUtils.defaultString(type); + private Element getQuestionById(Element focus, String ql) { + List list = getItems(focus); + for (Element item : list) { + String v = item.getNamedChildValue("linkId"); + if (ql.equals(v)) + return item; + Element tgt = getQuestionById(item, ql); + if (tgt != null) + return tgt; + } + return null; - if (entries.size() == 0) { - rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); + } + + private List getItems(Element element) { + List list = new ArrayList<>(); + element.getNamedChildren("item", list); + return list; + } + + private void checkLang(Element resource, NodeStack stack) { + String lang = resource.getNamedChildValue("language"); + if (!Utilities.noString(lang)) + stack.workingLang = lang; + } + + private void validateResourceRules(List errors, Element element, NodeStack stack) { + String lang = element.getNamedChildValue("language"); + Element text = element.getNamedChild("text"); + if (text != null) { + Element div = text.getNamedChild("div"); + if (lang != null && div != null) { + XhtmlNode xhtml = div.getXhtml(); + String l = xhtml.getAttribute("lang"); + String xl = xhtml.getAttribute("xml:lang"); + if (l == null && xl == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); } else { - // Get the first entry, the MessageHeader - Element firstEntry = entries.get(0); - // Get the stack of the first entry - NodeStack firstStack = stack.push(firstEntry, 1, null, null); - - String fullUrl = firstEntry.getNamedChildValue("fullUrl"); - - if (type.equals("document")) { - Element resource = firstEntry.getNamedChild("resource"); - String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { - validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); - } - checkAllInterlinked(errors, entries, stack, bundle, true); - } - if (type.equals("message")) { - Element resource = firstEntry.getNamedChild("resource"); - String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { - validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); - } - checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); - } - // We do not yet have rules requiring that the id and fullUrl match when dealing with messaging Bundles - // validateResourceIds(errors, entries, stack); + if (l == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); + } else if (!l.equals(lang)) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_"), lang, l); + } + if (xl == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); + } else if (!xl.equals(lang)) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_"), lang, xl); + } } - for (Element entry : entries) { - String fullUrl = entry.getNamedChildValue("fullUrl"); - String url = getCanonicalURLForEntry(entry); - String id = getIdForEntry(entry); - if (url != null) { - if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless the resource id (" + id + ") also matches"); - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless on the canonical server itself"); + } + } + // security tags are a set (system|code) + Element meta = element.getNamedChild("meta"); + if (meta != null) { + Set tags = new HashSet<>(); + List list = new ArrayList<>(); + meta.getNamedChildren("security", list); + int i = 0; + for (Element e : list) { + String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); + rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s),messages.getString("Duplicate_Security_Label_"), s); + tags.add(s); + i++; + } + } + } + + private void validateCapabilityStatement(List errors, Element cs, NodeStack stack) { + int iRest = 0; + for (Element rest : cs.getChildrenByName("rest")) { + int iResource = 0; + for (Element resource : rest.getChildrenByName("resource")) { + int iSP = 0; + for (Element searchParam : resource.getChildrenByName("searchParam")) { + String ref = searchParam.getChildValue("definition"); + String type = searchParam.getChildValue("type"); + if (!Utilities.noString(ref)) { + SearchParameter sp = context.fetchResource(SearchParameter.class, ref); + if (sp != null) { + rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", sp.getType().toCode().equals(type),messages.getString("Type_mismatch__SearchParameter__type_is__but_type_here_is_"), sp.getUrl(), sp.getType().toCode(), type); } - // todo: check specials + } + iSP++; } + iResource++; + } + iRest++; } + } - // hack for pre-UTG v2/v3 - private boolean isV3orV2Url(String url) { - return url.startsWith("http://hl7.org/fhir/v3/") || url.startsWith("http://hl7.org/fhir/v2/"); - } - - public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; - private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; - private static final String EXECUTION_ID = "validator.execution.id"; - - private String uriRegexForVersion() { - if (VersionUtilities.isR3Ver(context.getVersion())) - return URI_REGEX3; - else - return Constants.URI_REGEX; - } - - private String getCanonicalURLForEntry(Element entry) { - Element e = entry.getNamedChild("resource"); - if (e == null) - return null; - return e.getNamedChildValue("url"); - } - - private String getIdForEntry(Element entry) { - Element e = entry.getNamedChild("resource"); - if (e == null) - return null; - return e.getNamedChildValue("id"); - } - - /** - * Check each resource entry to ensure that the entry's fullURL includes the resource's id - * value. Adds an ERROR ValidationMessge to errors List for a given entry if it references - * a resource and fullURL does not include the resource's id. - * - * @param errors List of ValidationMessage objects that new errors will be added to. - * @param entries List of entry Element objects to be checked. - * @param stack Current NodeStack used to create path names in error detail messages. - */ - private void validateResourceIds(List errors, List entries, NodeStack stack) { - // TODO: Need to handle _version - int i = 1; - for (Element entry : entries) { - String fullUrl = entry.getNamedChildValue("fullUrl"); - Element resource = entry.getNamedChild("resource"); - String id = resource != null ? resource.getNamedChildValue("id") : null; - if (id != null && fullUrl != null) { - String urlId = null; - if (fullUrl.startsWith("https://") || fullUrl.startsWith("http://")) { - urlId = fullUrl.substring(fullUrl.lastIndexOf('/') + 1); - } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { - urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); - } - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id), - "Resource ID does not match the ID in the entry full URL ('" + id + "' vs '" + fullUrl + "') "); + private void validateCodeSystem(List errors, Element cs, NodeStack stack) { + String url = cs.getNamedChildValue("url"); + String vsu = cs.getNamedChildValue("valueSet"); + if (!Utilities.noString(vsu)) { + ValueSet vs; + try { + vs = context.fetchResourceWithException(ValueSet.class, vsu); + } catch (FHIRException e) { + vs = null; + } + if (vs != null) { + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion"), url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1,messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include"), url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_"), url, vsu, vs.getCompose().getInclude().get(0).getSystem())) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details"), url, vsu); } - i++; + } + } // todo... try getting the value set the other way... + } + + private void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { + Element q = element.getNamedChild("questionnaire"); + String questionnaire = null; + if (q != null) { + /* + * q.getValue() is correct for R4 content, but we'll also accept the second + * option just in case we're validating raw STU3 content. Being lenient here + * isn't the end of the world since if someone is actually doing the reference + * wrong in R4 content it'll get flagged elsewhere by the validator too + */ + if (isNotBlank(q.getValue())) { + questionnaire = q.getValue(); + } else if (isNotBlank(q.getChildValue("reference"))) { + questionnaire = q.getChildValue("reference"); + } + } + if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null,messages.getString("No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire"))) { + long t = System.nanoTime(); + Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); + sdTime = sdTime + (System.nanoTime() - t); + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null,messages.getString("The_questionnaire__could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire"), questionnaire)) { + boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); + validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); + } + } + } + + private Questionnaire loadQuestionnaire(Element resource, String id) throws FHIRException { + try { + for (Element contained : resource.getChildren("contained")) { + if (contained.getIdBase().equals(id)) { + FhirPublication v = FhirPublication.fromCode(context.getVersion()); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id); + byte[] json = bs.toByteArray(); + switch (v) { + case DSTU1: + throw new FHIRException("Unsupported version R1"); + case DSTU2: + org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); + Resource r5 = VersionConvertor_10_50.convertResource(r2); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case DSTU2016May: + org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); + r5 = VersionConvertor_14_50.convertResource(r2a); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case STU3: + org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); + r5 = VersionConvertor_30_50.convertResource(r3, false); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case R4: + org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); + r5 = VersionConvertor_40_50.convertResource(r4); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case R5: + r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + } } + } + return null; + } catch (IOException e) { + throw new FHIRException(e); + } + } + + private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + String text = element.getNamedChildValue("text"); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()),messages.getString("If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_"), qItem.getLinkId()); + + List answers = new ArrayList(); + element.getNamedChildren("answer", answers); + if (inProgress) + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); + else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); + } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled + // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? + // GDG 2019-07-13 +// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers),messages.getString("Item_has_answer_2_even_though_it_is_not_enabled_"), qItem.getLinkId()); } - private void checkAllInterlinked(List errors, List entries, NodeStack stack, Element bundle, boolean isError) { - List entryList = new ArrayList<>(); - for (Element entry : entries) { - Element r = entry.getNamedChild("resource"); - if (r != null) { - entryList.add(new EntrySummary(entry, r)); - } - } - for (EntrySummary e : entryList) { - Set references = findReferences(e.getEntry()); - for (String ref : references) { - Element tgt = resolveInBundle(entries, ref, e.getEntry().getChildValue("fullUrl"), e.getResource().fhirType(), e.getResource().getIdBase()); - if (tgt != null) { - EntrySummary t = entryForTarget(entryList, tgt); - if (t != null) { - e.getTargets().add(t); - } - } - } - } + if (answers.size() > 1) + rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_answer_item_with_this_linkId_allowed")); - Set visited = new HashSet<>(); - visitLinked(visited, entryList.get(0)); - boolean foundRevLinks; - do { - foundRevLinks = false; - for (EntrySummary e : entryList) { - if (!visited.contains(e)) { - boolean add = false; - for (EntrySummary t : e.getTargets()) { - if (visited.contains(t)) { - add = true; - } - } - if (add) { - foundRevLinks = true; - visitLinked(visited, e); - } - } + for (Element answer : answers) { + NodeStack ns = stack.push(answer, -1, null, null); + if (qItem.getType() != null) { + switch (qItem.getType()) { + case GROUP: + rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false,messages.getString("Items_of_type_group_should_not_have_answers")); + break; + case DISPLAY: // nothing + break; + case BOOLEAN: + validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); + break; + case DECIMAL: + validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); + break; + case INTEGER: + validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); + break; + case DATE: + validateQuestionnaireResponseItemType(errors, answer, ns, "date"); + break; + case DATETIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); + break; + case TIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "time"); + break; + case STRING: + validateQuestionnaireResponseItemType(errors, answer, ns, "string"); + break; + case TEXT: + validateQuestionnaireResponseItemType(errors, answer, ns, "text"); + break; + case URL: + validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); + break; + case ATTACHMENT: + validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); + break; + case REFERENCE: + validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); + break; + case QUANTITY: + if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) + if (qItem.hasExtension("???")) + validateQuestionnaireResponseItemQuantity(errors, answer, ns); + break; + case CHOICE: + String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) + checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); } - } while (foundRevLinks); + break; + case OPENCHOICE: + itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) + checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) + checkOption(errors, answer, ns, qsrc, qItem, "string", true); + } + break; +// case QUESTION: + case NULL: + // no validation + break; + } + } + validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); + } + if (qItem.getType() == null) { + fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Definition_for_item__does_not_contain_a_type"), qItem.getLinkId()); + } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { + List items = new ArrayList(); + element.getNamedChildren("item", items); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(),messages.getString("Items_not_of_type_DISPLAY_should_not_have_items__linkId_0"), qItem.getLinkId()); + } else { + validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); + } + } + private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { + return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; + } + + private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + if (elements.size() > 1) + rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_item_with_this_linkId_allowed__"), qItem.getLinkId()); + int i = 0; + for (Element element : elements) { + NodeStack ns = stack.push(element, i, null, null); + validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); + i++; + } + } + + private int getLinkIdIndex(List qItems, String linkId) { + for (int i = 0; i < qItems.size(); i++) { + if (linkId.equals(qItems.get(i).getLinkId())) + return i; + } + return -1; + } + + private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + List items = new ArrayList(); + element.getNamedChildren("item", items); + // now, sort into stacks + Map> map = new HashMap>(); + int lastIndex = -1; + for (Element item : items) { + String linkId = item.getNamedChildValue("linkId"); + if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId),messages.getString("No_LinkId_so_cant_be_validated"))) { + int index = getLinkIdIndex(qItems, linkId); + if (index == -1) { + QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); + if (qItem != null) { + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); + NodeStack ns = stack.push(item, -1, null, null); + validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); + } else + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1,messages.getString("LinkId__not_found_in_questionnaire"), linkId); + } else { + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex,messages.getString("Structural_Error_items_are_out_of_order")); + lastIndex = index; + + // If an item has a child called "linkId" but no child called "answer", + // we'll treat it as not existing for the purposes of enableWhen validation + if (item.hasChildren("answer") || item.hasChildren("item")) { + List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); + mapItem.add(item); + } + } + } + } + + // ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order + for (QuestionnaireItemComponent qItem : qItems) { + List mapItem = map.get(qItem.getLinkId()); + validateQuestionnaireResponseItem(hostContext, qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); + } + } + + public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { + boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe); + if (mapItem != null) { + if (!enabled) { int i = 0; - for (EntrySummary e : entryList) { - Element entry = e.getEntry(); - if (isError) { - rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); - } else { - warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); - } - i++; + for (Element e : mapItem) { + NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); + rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled,messages.getString("Item_has_answer_even_though_it_is_not_enabled_item_id__"), qItem.getLinkId()); + i++; } + } + + // Recursively validate child items + validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); + + } else { + + // item is missing, is the question enabled? + if (enabled && qItem.getRequired()) { + String message = "No response found for required item with id = '" + qItem.getLinkId() + "'"; + if (inProgress) { + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } else { + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } + } + } - private EntrySummary entryForTarget(List entryList, Element tgt) { - for (EntrySummary e : entryList) { - if (e.getEntry() == tgt) { - return e; - } + } + + private String misplacedItemError(QuestionnaireItemComponent qItem) { + return qItem.hasLinkId() ? String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId()) : "Structural Error: item is in the wrong place"; + } + + private void validateQuestionnaireResponseItemQuantity(List errors, Element answer, NodeStack stack) { + + } + + private String validateQuestionnaireResponseItemType(List errors, Element element, NodeStack stack, String... types) { + List values = new ArrayList(); + element.getNamedChildrenWithWildcard("value[x]", values); + for (int i = 0; i < types.length; i++) { + if (types[i].equals("text")) { + types[i] = "string"; + } + } + if (values.size() > 0) { + NodeStack ns = stack.push(values.get(0), -1, null, null); + CommaSeparatedStringBuilder l = new CommaSeparatedStringBuilder(); + for (String s : types) { + l.append(s); + if (values.get(0).getName().equals("value" + Utilities.capitalize(s))) + return (s); + } + if (types.length == 1) + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_of_type_"), types[0]); + else + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_one_of_the_types_"), l.toString()); + } + return null; + } + + private QuestionnaireItemComponent findQuestionnaireItem(Questionnaire qSrc, String linkId) { + return findItem(qSrc.getItem(), linkId); + } + + private QuestionnaireItemComponent findItem(List list, String linkId) { + for (QuestionnaireItemComponent item : list) { + if (linkId.equals(item.getLinkId())) + return item; + QuestionnaireItemComponent result = findItem(item.getItem(), linkId); + if (result != null) + return result; + } + return null; + } + + private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { + ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); + if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(ref))) { + try { + Coding c = ObjectConverter.readAsCoding(value); + if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { + if (theOpenChoice) { + return; + } } + + long t = System.nanoTime(); + ValidationResult res = context.validateCode(new ValidationOptions(stack.workingLang), c, vs); + txTime = txTime + (System.nanoTime() - t); + if (!res.isOk()) { + txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire"); + } else if (res.getSeverity() != null) { + super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false,messages.getString("Error__validating_Coding_against_Questionnaire_Options"), e.getMessage()); + } + } + } + + private void validateAnswerCode(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) { + Element v = answer.getNamedChild("valueCoding"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) + checkCodingOption(errors, answer, stack, qSrc, qItem, theOpenChoice); + // validateAnswerCode(errors, v, stack, qItem.getOption()); + else if (qItem.hasAnswerValueSet()) + validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); + else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_options_because_no_option_or_options_are_provided")); + } + + private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { + checkOption(errors, answer, stack, qSrc, qItem, type, false); + } + + private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { + if (type.equals("integer")) checkIntegerOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("date")) checkDateOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("time")) checkTimeOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("string")) checkStringOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("Coding")) checkCodingOption(errors, answer, stack, qSrc, qItem, openChoice); + } + + private void checkIntegerOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueInteger"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueIntegerType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_integer")); + } else { + boolean found = false; + for (IntegerType item : list) { + if (item.getValue() == Integer.parseInt(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_integer__is_not_a_valid_option"), v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_integer_answer_option_because_no_option_list_is_provided")); + } + + private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueDate"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueDateType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_date")); + } else { + boolean found = false; + for (DateType item : list) { + if (item.getValue().equals(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_date__is_not_a_valid_option"), v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_date_answer_option_because_no_option_list_is_provided")); + } + + private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueTime"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueTimeType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_time")); + } else { + boolean found = false; + for (TimeType item : list) { + if (item.getValue().equals(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_time__is_not_a_valid_option"), v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_time_answer_option_because_no_option_list_is_provided")); + } + + private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueString"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + if (components.getValue() != null) { + list.add(components.getValueStringType()); + } + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (!openChoice) { + if (list.isEmpty()) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_string")); + } else { + boolean found = false; + for (StringType item : list) { + if (item.getValue().equals((v.primitiveValue()))) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_string__is_not_a_valid_option"), v.primitiveValue()); + } + } + } + } else { + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_string_answer_option_because_no_option_list_is_provided")); + } + } + + private void checkCodingOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueCoding"); + String system = v.getNamedChildValue("system"); + String code = v.getNamedChildValue("code"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + if (components.getValue() != null) { + list.add(components.getValueCoding()); + } + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_coding")); + } else { + boolean found = false; + for (Coding item : list) { + if (ObjectUtil.equals(item.getSystem(), system) && ObjectUtil.equals(item.getCode(), code)) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_code__is_not_a_valid_option"), system, code); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_Coding_option_because_no_option_list_is_provided")); + } + + private String tail(String path) { + return path.substring(path.lastIndexOf(".") + 1); + } + + private String tryParse(String ref) { + String[] parts = ref.split("\\/"); + switch (parts.length) { + case 1: return null; + case 2: + return checkResourceType(parts[0]); + default: + if (parts[parts.length - 2].equals("_history")) + return checkResourceType(parts[parts.length - 4]); + else + return checkResourceType(parts[parts.length - 2]); } + } - private void visitLinked(Set visited, EntrySummary t) { - if (!visited.contains(t)) { - visited.add(t); - for (EntrySummary e : t.getTargets()) { - visitLinked(visited, e); - } + private boolean typesAreAllReference(List theType) { + for (TypeRefComponent typeRefComponent : theType) { + if (typeRefComponent.getCode().equals("Reference") == false) { + return false; + } + } + return true; + } + + private void validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials) { + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + String type = bundle.getNamedChildValue("type"); + type = StringUtils.defaultString(type); + + if (entries.size() == 0) { + rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")),messages.getString("Documents_or_Messages_must_contain_at_least_one_entry")); + } else { + // Get the first entry, the MessageHeader + Element firstEntry = entries.get(0); + // Get the stack of the first entry + NodeStack firstStack = stack.push(firstEntry, 1, null, null); + + String fullUrl = firstEntry.getNamedChildValue("fullUrl"); + + if (type.equals("document")) { + Element resource = firstEntry.getNamedChild("resource"); + String id = resource.getNamedChildValue("id"); + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { + validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } - } - - private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack) { - followResourceLinks(entry, visitedResources, candidateEntries, candidateResources, errors, stack, 0); - } - - private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack, int depth) { - Element resource = entry.getNamedChild("resource"); - if (visitedResources.containsValue(resource)) - return; - - visitedResources.put(entry.getNamedChildValue("fullUrl"), resource); - - String type = null; - Set references = findReferences(resource); - for (String reference : references) { - // We don't want errors when just retrieving the element as they will be caught (with better path info) in subsequent processing - IndexedElement r = getFromBundle(stack.getElement(), reference, entry.getChildValue("fullUrl"), new ArrayList(), stack.addToLiteralPath("entry[" + candidateResources.indexOf(resource) + "]"), type, "transaction".equals(stack.getElement().getChildValue("type"))); - if (r != null && !visitedResources.containsValue(r.getMatch())) { - followResourceLinks(candidateEntries.get(r.getMatch()), visitedResources, candidateEntries, candidateResources, errors, stack, depth + 1); - } + checkAllInterlinked(errors, entries, stack, bundle, true); + } + if (type.equals("message")) { + Element resource = firstEntry.getNamedChild("resource"); + String id = resource.getNamedChildValue("id"); + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { + validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } + checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); + } + // We do not yet have rules requiring that the id and fullUrl match when dealing with messaging Bundles + // validateResourceIds(errors, entries, stack); + } + for (Element entry : entries) { + String fullUrl = entry.getNamedChildValue("fullUrl"); + String url = getCanonicalURLForEntry(entry); + String id = getIdForEntry(entry); + if (url != null) { + if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false,messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches"), url, fullUrl, id); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))),messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself"), url, fullUrl); + } + // todo: check specials + } + } + + // hack for pre-UTG v2/v3 + private boolean isV3orV2Url(String url) { + return url.startsWith("http://hl7.org/fhir/v3/") || url.startsWith("http://hl7.org/fhir/v2/"); + } + + public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; + private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; + private static final String EXECUTION_ID = "validator.execution.id"; + + private String uriRegexForVersion() { + if (VersionUtilities.isR3Ver(context.getVersion())) + return URI_REGEX3; + else + return Constants.URI_REGEX; + } + + private String getCanonicalURLForEntry(Element entry) { + Element e = entry.getNamedChild("resource"); + if (e == null) + return null; + return e.getNamedChildValue("url"); + } + + private String getIdForEntry(Element entry) { + Element e = entry.getNamedChild("resource"); + if (e == null) + return null; + return e.getNamedChildValue("id"); + } + + /** + * Check each resource entry to ensure that the entry's fullURL includes the resource's id + * value. Adds an ERROR ValidationMessge to errors List for a given entry if it references + * a resource and fullURL does not include the resource's id. + * + * @param errors List of ValidationMessage objects that new errors will be added to. + * @param entries List of entry Element objects to be checked. + * @param stack Current NodeStack used to create path names in error detail messages. + */ + private void validateResourceIds(List errors, List entries, NodeStack stack) { + // TODO: Need to handle _version + int i = 1; + for (Element entry : entries) { + String fullUrl = entry.getNamedChildValue("fullUrl"); + Element resource = entry.getNamedChild("resource"); + String id = resource != null ? resource.getNamedChildValue("id") : null; + if (id != null && fullUrl != null) { + String urlId = null; + if (fullUrl.startsWith("https://") || fullUrl.startsWith("http://")) { + urlId = fullUrl.substring(fullUrl.lastIndexOf('/') + 1); + } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { + urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); + } + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id),messages.getString("Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__"), id, fullUrl); + } + i++; + } + } + + private void checkAllInterlinked(List errors, List entries, NodeStack stack, Element bundle, boolean isError) { + List entryList = new ArrayList<>(); + for (Element entry : entries) { + Element r = entry.getNamedChild("resource"); + if (r != null) { + entryList.add(new EntrySummary(entry, r)); + } + } + for (EntrySummary e : entryList) { + Set references = findReferences(e.getEntry()); + for (String ref : references) { + Element tgt = resolveInBundle(entries, ref, e.getEntry().getChildValue("fullUrl"), e.getResource().fhirType(), e.getResource().getIdBase()); + if (tgt != null) { + EntrySummary t = entryForTarget(entryList, tgt); + if (t != null) { + e.getTargets().add(t); + } + } + } } - private Set findReferences(Element start) { - Set references = new HashSet(); - findReferences(start, references); - return references; + Set visited = new HashSet<>(); + visitLinked(visited, entryList.get(0)); + boolean foundRevLinks; + do { + foundRevLinks = false; + for (EntrySummary e : entryList) { + if (!visited.contains(e)) { + boolean add = false; + for (EntrySummary t : e.getTargets()) { + if (visited.contains(t)) { + add = true; + } + } + if (add) { + foundRevLinks = true; + visitLinked(visited, e); + } + } + } + } while (foundRevLinks); + + int i = 0; + for (EntrySummary e : entryList) { + Element entry = e.getEntry(); + if (isError) { + rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + } else { + warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + } + i++; + } + } + + private EntrySummary entryForTarget(List entryList, Element tgt) { + for (EntrySummary e : entryList) { + if (e.getEntry() == tgt) { + return e; + } + } + return null; + } + + private void visitLinked(Set visited, EntrySummary t) { + if (!visited.contains(t)) { + visited.add(t); + for (EntrySummary e : t.getTargets()) { + visitLinked(visited, e); + } + } + } + + private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack) { + followResourceLinks(entry, visitedResources, candidateEntries, candidateResources, errors, stack, 0); + } + + private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack, int depth) { + Element resource = entry.getNamedChild("resource"); + if (visitedResources.containsValue(resource)) + return; + + visitedResources.put(entry.getNamedChildValue("fullUrl"), resource); + + String type = null; + Set references = findReferences(resource); + for (String reference : references) { + // We don't want errors when just retrieving the element as they will be caught (with better path info) in subsequent processing + IndexedElement r = getFromBundle(stack.getElement(), reference, entry.getChildValue("fullUrl"), new ArrayList(), stack.addToLiteralPath("entry[" + candidateResources.indexOf(resource) + "]"), type, "transaction".equals(stack.getElement().getChildValue("type"))); + if (r != null && !visitedResources.containsValue(r.getMatch())) { + followResourceLinks(candidateEntries.get(r.getMatch()), visitedResources, candidateEntries, candidateResources, errors, stack, depth + 1); + } + } + } + + private Set findReferences(Element start) { + Set references = new HashSet(); + findReferences(start, references); + return references; + } + + private void findReferences(Element start, Set references) { + for (Element child : start.getChildren()) { + if (child.getType().equals("Reference")) { + String ref = child.getChildValue("reference"); + if (ref != null && !ref.startsWith("#")) + references.add(ref); + } + if (child.getType().equals("url") || child.getType().equals("uri") || child.getType().equals("canonical")) { + String ref = child.primitiveValue(); + if (ref != null && !ref.startsWith("#")) + references.add(ref); + } + findReferences(child, references); + } + } + + private void validateBundleReference(List errors, List entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) { + String reference = null; + try { + reference = ref.getNamedChildValue("reference"); + } catch (Error e) { + } - private void findReferences(Element start, Set references) { - for (Element child : start.getChildren()) { - if (child.getType().equals("Reference")) { - String ref = child.getChildValue("reference"); - if (ref != null && !ref.startsWith("#")) - references.add(ref); - } - if (child.getType().equals("url") || child.getType().equals("uri") || child.getType().equals("canonical")) { - String ref = child.primitiveValue(); - if (ref != null && !ref.startsWith("#")) - references.add(ref); - } - findReferences(child, references); + if (ref != null && !Utilities.noString(reference)) { + Element target = resolveInBundle(entries, reference, fullUrl, type, id); + rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null,messages.getString("Cant_find__in_the_bundle_"), reference, name); + } + } + + private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { + String resourceName = element.getType(); + TypeRefComponent trr = null; + for (TypeRefComponent tr : child.getType()) { + if (tr.getCode().equals("Resource")) { + trr = tr; + break; + } + } + if (trr == null) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__no_resources_allowed_here"), resourceName); + } else if (isValidResourceType(resourceName, trr)) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); + sdTime = sdTime + (System.nanoTime() - t); + // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise + ValidatorHostContext hc = null; + if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) { + resource = element; + hc = hostContext.forEntry(element); + } else { + hc = hostContext.forContained(element); + } + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null,messages.getString("No_profile_found_for_contained_resource_of_type_"), resourceName)) { + validateResource(hc, errors, resource, element, profile, idstatus, stack); + } + } else { + List types = new ArrayList<>(); + for (UriType u : trr.getProfile()) { + StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); + if (sd != null && !types.contains(sd.getType())) { + types.add(sd.getType()); } + } + if (types.size() == 1) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_"), resourceName, types.get(0)); + } else { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_one_of_"), resourceName, types); + } + } + } + + private boolean isValidResourceType(String type, TypeRefComponent def) { + if (!def.hasProfile()) { + return true; + } + List list = new ArrayList<>(); + for (UriType u : def.getProfile()) { + StructureDefinition sdt = context.fetchResource(StructureDefinition.class, u.getValue()); + if (sdt != null) { + list.add(sdt); + } } - private void validateBundleReference(List errors, List entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) { - String reference = null; - try { - reference = ref.getNamedChildValue("reference"); - } catch (Error e) { - - } - - if (ref != null && !Utilities.noString(reference)) { - Element target = resolveInBundle(entries, reference, fullUrl, type, id); - rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, "Can't find '" + reference + "' in the bundle (" + name + ")"); - } - } - - private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { - String resourceName = element.getType(); - TypeRefComponent trr = null; - for (TypeRefComponent tr : child.getType()) { - if (tr.getCode().equals("Resource")) { - trr = tr; - break; - } - } - if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + " is not valid - no resources allowed here"); - } else if (isValidResourceType(resourceName, trr)) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); - sdTime = sdTime + (System.nanoTime() - t); - // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise - ValidatorHostContext hc = null; - if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) { - resource = element; - hc = hostContext.forEntry(element); - } else { - hc = hostContext.forContained(element); - } - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'")) { - validateResource(hc, errors, resource, element, profile, idstatus, stack); - } - } else { - List types = new ArrayList<>(); - for (UriType u : trr.getProfile()) { - StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); - if (sd != null && !types.contains(sd.getType())) { - types.add(sd.getType()); - } - } - if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be " + types.get(0)); - } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be one of " + types); - } - } - } - - private boolean isValidResourceType(String type, TypeRefComponent def) { - if (!def.hasProfile()) { + StructureDefinition sdt = context.fetchTypeDefinition(type); + while (sdt != null) { + if (def.getWorkingCode().equals("Resource")) { + for (StructureDefinition sd : list) { + if (sd.getUrl().equals(sdt.getUrl())) { return true; + } + if (sd.getType().equals(sdt.getType())) { + return true; + } } - List list = new ArrayList<>(); - for (UriType u : def.getProfile()) { - StructureDefinition sdt = context.fetchResource(StructureDefinition.class, u.getValue()); - if (sdt != null) { - list.add(sdt); - } - } + } + sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition()); + } + return false; + } - StructureDefinition sdt = context.fetchTypeDefinition(type); - while (sdt != null) { - if (def.getWorkingCode().equals("Resource")) { - for (StructureDefinition sd : list) { - if (sd.getUrl().equals(sdt.getUrl())) { - return true; - } - if (sd.getType().equals(sdt.getType())) { - return true; - } - } - } - sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition()); - } - return false; + private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { + // first entry must be a composition + if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"),messages.getString("The_first_entry_in_a_document_must_be_a_composition"))) { + + // the composition subject etc references must resolve in the bundle + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, true, "author", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "encounter", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "custodian", "Composition"); + validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party"); + validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail"); + + validateSections(errors, entries, composition, stack, fullUrl, id); + } + } + + public void validateDocumentSubReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) { + List list = new ArrayList<>(); + composition.getNamedChildren(parent, list); + int i = 1; + for (Element elem : list) { + validateDocumentReference(errors, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent); + i++; + } + } + + public void validateDocumentReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) { + if (repeats) { + List list = new ArrayList<>(); + composition.getNamedChildren(propName, list); + int i = 1; + for (Element elem : list) { + validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id); + i++; + } + + } else { + Element elem = composition.getNamedChild(propName); + if (elem != null) { + validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id); + } + } + } + + 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, boolean checkDisplayInContext, String extensionUrl) throws FHIRException { + + // check type invariants + checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); + if (definition.getFixed() != null) + checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); + + // get the list of direct defined children, including slices + List childDefinitions = ProfileUtilities.getChildMap(profile, definition); + if (childDefinitions.isEmpty()) { + if (actualType == null) + return; // there'll be an error elsewhere in this case, and we're going to stop. + childDefinitions = getActualTypeChildren(hostContext, element, actualType); + } else if (definition.getType().size() > 1) { + // this only happens when the profile constrains the abstract children but leaves th choice open. + if (actualType == null) + return; // there'll be an error elsewhere in this case, and we're going to stop. + List typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); + // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) + mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); } - private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { - // first entry must be a composition - if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), - "The first entry in a document must be a composition")) { + List children = listChildren(element, stack); + List problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children); - // the composition subject etc references must resolve in the bundle - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, true, "author", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "encounter", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "custodian", "Composition"); - validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party"); - validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail"); + checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths); + // 4. check order if any slices are ordered. (todo) - validateSections(errors, entries, composition, stack, fullUrl, id); + // 5. inspect each child for validity + for (ElementInfo ei : children) { + checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl); + } + } + + private void mergeChildLists(List master, List additional, String masterPath, String typePath) { + for (ElementDefinition ed : additional) { + boolean inMaster = false; + for (ElementDefinition t : master) { + String tp = masterPath + ed.getPath().substring(typePath.length()); + if (t.getPath().equals(tp)) { + inMaster = true; } + } + if (!inMaster) { + master.add(ed); + } } - public void validateDocumentSubReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) { - List list = new ArrayList<>(); - composition.getNamedChildren(parent, list); - int i = 1; - for (Element elem : list) { - validateDocumentReference(errors, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent); - i++; + + } + + // todo: the element definition in context might assign a constrained profile for the type? + public List getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { + List childDefinitions; + StructureDefinition dt = null; + if (isAbsolute(actualType)) + dt = this.context.fetchResource(StructureDefinition.class, actualType); + else + dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); + if (dt == null) + throw new DefinitionException("Unable to resolve actual type " + actualType); + trackUsage(dt, hostContext, element); + + childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); + return childDefinitions; + } + + public void checkChild(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, + Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) + throws FHIRException, DefinitionException { + + List profiles = new ArrayList(); + if (ei.definition != null) { + String type = null; + ElementDefinition typeDefn = null; + checkMustSupport(profile, ei); + + if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getWorkingCode()) && !"Element".equals(ei.definition.getType().get(0).getWorkingCode()) + && !"BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode())) { + type = ei.definition.getType().get(0).getWorkingCode(); + // Excluding reference is a kludge to get around versioning issues + if (ei.definition.getType().get(0).hasProfile()) { + for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { + profiles.add(p.getValue()); + } } - } - - public void validateDocumentReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) { - if (repeats) { - List list = new ArrayList<>(); - composition.getNamedChildren(propName, list); - int i = 1; - for (Element elem : list) { - validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id); - i++; - } - - } else { - Element elem = composition.getNamedChild(propName); - if (elem != null) { - validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id); - } + } else if (ei.definition.getType().size() == 1 && "*".equals(ei.definition.getType().get(0).getWorkingCode())) { + String prefix = tail(ei.definition.getPath()); + assert prefix.endsWith("[x]"); + type = ei.getName().substring(prefix.length() - 3); + if (isPrimitiveType(type)) + type = Utilities.uncapitalize(type); + if (ei.definition.getType().get(0).hasProfile()) { + for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { + profiles.add(p.getValue()); + } } - } + } else if (ei.definition.getType().size() > 1) { - 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, boolean checkDisplayInContext, String extensionUrl) throws FHIRException { + String prefix = tail(ei.definition.getPath()); + assert typesAreAllReference(ei.definition.getType()) || ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; - // check type invariants - checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); - if (definition.getFixed() != null) - checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); - - // get the list of direct defined children, including slices - List childDefinitions = ProfileUtilities.getChildMap(profile, definition); - if (childDefinitions.isEmpty()) { - if (actualType == null) - return; // there'll be an error elsewhere in this case, and we're going to stop. - childDefinitions = getActualTypeChildren(hostContext, element, actualType); - } else if (definition.getType().size() > 1) { - // this only happens when the profile constrains the abstract children but leaves th choice open. - if (actualType == null) - return; // there'll be an error elsewhere in this case, and we're going to stop. - List typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); - // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) - mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); - } - - List children = listChildren(element, stack); - List problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children); - - checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths); - // 4. check order if any slices are ordered. (todo) - - // 5. inspect each child for validity - for (ElementInfo ei : children) { - checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl); - } - } - - private void mergeChildLists(List master, List additional, String masterPath, String typePath) { - for (ElementDefinition ed : additional) { - boolean inMaster = false; - for (ElementDefinition t : master) { - String tp = masterPath + ed.getPath().substring(typePath.length()); - if (t.getPath().equals(tp)) { - inMaster = true; - } - } - if (!inMaster) { - master.add(ed); - } - } - - - } - - // todo: the element definition in context might assign a constrained profile for the type? - public List getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { - List childDefinitions; - StructureDefinition dt = null; - if (isAbsolute(actualType)) - dt = this.context.fetchResource(StructureDefinition.class, actualType); - else - dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); - if (dt == null) - throw new DefinitionException("Unable to resolve actual type " + actualType); - trackUsage(dt, hostContext, element); - - childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); - return childDefinitions; - } - - public void checkChild(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, - Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) - throws FHIRException, DefinitionException { - - List profiles = new ArrayList(); - if (ei.definition != null) { - String type = null; - ElementDefinition typeDefn = null; - checkMustSupport(profile, ei); - - if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getWorkingCode()) && !"Element".equals(ei.definition.getType().get(0).getWorkingCode()) - && !"BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode())) { - type = ei.definition.getType().get(0).getWorkingCode(); - // Excluding reference is a kludge to get around versioning issues - if (ei.definition.getType().get(0).hasProfile()) { - for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { - profiles.add(p.getValue()); - } - } - } else if (ei.definition.getType().size() == 1 && "*".equals(ei.definition.getType().get(0).getWorkingCode())) { - String prefix = tail(ei.definition.getPath()); - assert prefix.endsWith("[x]"); - type = ei.getName().substring(prefix.length() - 3); - if (isPrimitiveType(type)) - type = Utilities.uncapitalize(type); - if (ei.definition.getType().get(0).hasProfile()) { - for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { - profiles.add(p.getValue()); - } - } - } else if (ei.definition.getType().size() > 1) { - - String prefix = tail(ei.definition.getPath()); - assert typesAreAllReference(ei.definition.getType()) || ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; - - if (ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR)) - type = ei.getElement().getType(); - else { - prefix = prefix.substring(0, prefix.length() - 3); - for (TypeRefComponent t : ei.definition.getType()) - if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { - type = t.getWorkingCode(); - // Excluding reference is a kludge to get around versioning issues - if (t.hasProfile() && !type.equals("Reference")) - profiles.add(t.getProfile().get(0).getValue()); - } - } - if (type == null) { - TypeRefComponent trc = ei.definition.getType().get(0); - if (trc.getWorkingCode().equals("Reference")) - type = "Reference"; - else - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, - "The type of element " + ei.getName() + " is not known, which is illegal. Valid types at this point are " + describeTypes(ei.definition.getType())); - } - } else if (ei.definition.getContentReference() != null) { - typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); - } else if (ei.definition.getType().size() == 1 && ("Element".equals(ei.definition.getType().get(0).getWorkingCode()) || "BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode()))) { - if (ei.definition.getType().get(0).hasProfile()) { - CanonicalType pu = ei.definition.getType().get(0).getProfile().get(0); - if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) - profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT)); - else - profiles.add(pu.getValue()); - } - } - - if (type != null) { - if (type.startsWith("@")) { - ei.definition = findElement(profile, type.substring(1)); - type = null; - } - } - NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); - if (debug) { - System.out.println(" " + localStack.getLiteralPath()); - } - String localStackLiterapPath = localStack.getLiteralPath(); - String eiPath = ei.getPath(); - assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; - boolean thisIsCodeableConcept = false; - String thisExtension = null; - boolean checkDisplay = true; - - checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.getElement(), localStack, true); - - ei.getElement().markValidation(profile, ei.definition); - boolean elementValidated = false; - if (type != null) { - if (isPrimitiveType(type)) { - checkPrimitive(hostContext, errors, ei.getPath(), type, ei.definition, ei.getElement(), profile, stack); - } else { - if (ei.definition.hasFixed()) { - checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getFixed(), profile.getUrl(), ei.definition.getSliceName(), null); - } - if (ei.definition.hasPattern()) { - checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getPattern(), profile.getUrl(), ei.definition.getSliceName(), null, true); - } - } - if (type.equals("Identifier")) { - checkIdentifier(errors, ei.getPath(), ei.getElement(), ei.definition); - } else if (type.equals("Coding")) { - checkCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack); - } else if (type.equals("CodeableConcept")) { - checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack); - thisIsCodeableConcept = true; - } else if (type.equals("Reference")) { - checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, ei.definition, actualType, localStack); - // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension - } else if (type.equals("Extension")) { - Element eurl = ei.getElement().getNamedChild("url"); - if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, "Extension.url is required")) { - String url = eurl.primitiveValue(); - thisExtension = url; - if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), "Extension.url is required")) { - if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), "Extension.url must be an absolute URL")) { - checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); - } - } - } - } else if (type.equals("Resource")) { - validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if - elementValidated = true; - // (str.matches(".*([.,/])work\\1$")) - } else if (Utilities.isAbsoluteUrl(type)) { - StructureDefinition defn = context.fetchTypeDefinition(type); - if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) { - List txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep()); - if (txtype.contains("CodeableConcept")) { - checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack, defn); - thisIsCodeableConcept = true; - } else if (txtype.contains("Coding")) { - checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn); - } - } - } - } else { - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.getName())) - validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); - } - StructureDefinition p = null; - String tail = null; - if (profiles.isEmpty()) { - if (type != null) { - p = getProfileForType(type, ei.definition.getType()); - - // If dealing with a primitive type, then we need to check the current child against - // the invariants (constraints) on the current element, because otherwise it only gets - // checked against the primary type's invariants: LLoyd - //if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { - // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); - //} - - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown type " + type); - } - } else if (profiles.size() == 1) { - String url = profiles.get(0); - if (url.contains("#")) { - tail = url.substring(url.indexOf("#") + 1); - url = url.substring(0, url.indexOf("#")); - } - p = this.context.fetchResource(StructureDefinition.class, url); - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + profiles.get(0)); - } else { - elementValidated = true; - HashMap> goodProfiles = new HashMap>(); - HashMap> badProfiles = new HashMap>(); - for (String typeProfile : profiles) { - String url = typeProfile; - tail = null; - if (url.contains("#")) { - tail = url.substring(url.indexOf("#") + 1); - url = url.substring(0, url.indexOf("#")); - } - p = this.context.fetchResource(StructureDefinition.class, typeProfile); - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + typeProfile)) { - List profileErrors = new ArrayList(); - validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - if (hasErrors(profileErrors)) - badProfiles.put(typeProfile, profileErrors); - else - goodProfiles.put(typeProfile, profileErrors); - } - } - if (goodProfiles.size() == 1) { - errors.addAll(goodProfiles.values().iterator().next()); - } else if (goodProfiles.size() == 0) { - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Unable to find matching profile among choices: " + StringUtils.join("; ", profiles)); - for (String m : badProfiles.keySet()) { - p = this.context.fetchResource(StructureDefinition.class, m); - for (ValidationMessage message : badProfiles.get(m)) { - message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); - errors.add(message); - } - } - } else { - warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Found multiple matching profiles among choices: " + StringUtils.join("; ", goodProfiles.keySet())); - for (String m : goodProfiles.keySet()) { - p = this.context.fetchResource(StructureDefinition.class, m); - for (ValidationMessage message : goodProfiles.get(m)) { - message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); - errors.add(message); - } - } - } - } - if (p != null) { - trackUsage(p, hostContext, element); - - if (!elementValidated) { - if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - else - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - } - 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.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - } - } - } - } - - private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { - if (tracker != null) { - tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); - } - } - - private boolean hasMapping(String url, StructureDefinition defn, ElementDefinition elem) { - String id = null; - for (StructureDefinitionMappingComponent m : defn.getMapping()) { - if (url.equals(m.getUri())) { - id = m.getIdentity(); - break; - } - } - if (id != null) { - for (ElementDefinitionMappingComponent m : elem.getMapping()) { - if (id.equals(m.getIdentity())) { - return true; - } - } - - } - return false; - } - - private List getMapping(String url, StructureDefinition defn, ElementDefinition elem) { - List res = new ArrayList<>(); - String id = null; - for (StructureDefinitionMappingComponent m : defn.getMapping()) { - if (url.equals(m.getUri())) { - id = m.getIdentity(); - break; - } - } - if (id != null) { - for (ElementDefinitionMappingComponent m : elem.getMapping()) { - if (id.equals(m.getIdentity())) { - res.add(m.getMap()); - } - } - } - return res; - } - - public void checkMustSupport(StructureDefinition profile, ElementInfo ei) { - String usesMustSupport = profile.getUserString("usesMustSupport"); - if (usesMustSupport == null) { - usesMustSupport = "N"; - for (ElementDefinition pe : profile.getSnapshot().getElement()) { - if (pe.getMustSupport()) { - usesMustSupport = "Y"; - break; - } - } - profile.setUserData("usesMustSupport", usesMustSupport); - } - if (usesMustSupport.equals("Y")) { - String elementSupported = ei.getElement().getUserString("elementSupported"); - if (elementSupported == null || ei.definition.getMustSupport()) - if (ei.definition.getMustSupport()) { - ei.getElement().setUserData("elementSupported", "Y"); - } - } - } - - public void checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, - List childDefinitions, List children, List problematicPaths) throws DefinitionException { - // 3. report any definitions that have a cardinality problem - for (ElementDefinition ed : childDefinitions) { - if (ed.getRepresentation().isEmpty()) { // ignore xml attributes - int count = 0; - List slices = null; - if (ed.hasSlicing()) - slices = ProfileUtilities.getSliceList(profile, ed); - for (ElementInfo ei : children) - if (ei.definition == ed) - count++; - else if (slices != null) { - for (ElementDefinition sed : slices) { - if (ei.definition == sed) { - count++; - break; - } - } - } - String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; - if (ed.getMin() > 0) { - if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + "': Unable to check minimum required (" + Integer.toString(ed.getMin()) + ") due to lack of slicing validation"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + ": minimum required = " + Integer.toString(ed.getMin()) + ", but only found " + Integer.toString(count)); - } - if (ed.hasMax() && !ed.getMax().equals("*")) { - if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": Unable to check max allowed (" + ed.getMax() + ") due to lack of slicing validation"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": max allowed = " + ed.getMax() + ", but found " + Integer.toString(count)); - } - } - } - } - - public List assignChildren(ValidatorHostContext hostContext, List errors, StructureDefinition profile, Element resource, - NodeStack stack, List childDefinitions, List children) throws DefinitionException { - // 2. assign children to a definition - // for each definition, for each child, check whether it belongs in the slice - ElementDefinition slicer = null; - boolean unsupportedSlicing = false; - List problematicPaths = new ArrayList(); - String slicingPath = null; - int sliceOffset = 0; - for (int i = 0; i < childDefinitions.size(); i++) { - ElementDefinition ed = childDefinitions.get(i); - boolean childUnsupportedSlicing = false; - boolean process = true; - if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) - slicingPath = ed.getPath(); - else if (slicingPath != null && ed.getPath().equals(slicingPath)) - ; // nothing - else if (slicingPath != null && !ed.getPath().startsWith(slicingPath)) - slicingPath = null; - // where are we with slicing - if (ed.hasSlicing()) { - if (slicer != null && slicer.getPath().equals(ed.getPath())) { - String errorContext = "profile " + profile.getUrl(); - if (!resource.getChildValue("id").isEmpty()) - errorContext += "; instance " + resource.getChildValue("id"); - throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); - } - slicer = ed; - process = false; - sliceOffset = i; - } else if (slicer != null && !slicer.getPath().equals(ed.getPath())) - slicer = null; - - for (ElementInfo ei : children) { - if (ei.sliceInfo == null) { - ei.sliceInfo = new ArrayList<>(); - } - unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei); - } - } - int last = -1; - int lastSlice = -1; - for (ElementInfo ei : children) { - String sliceInfo = ""; - if (slicer != null) - sliceInfo = " (slice: " + slicer.getPath() + ")"; - if (!unsupportedSlicing) - if (ei.additionalSlice && ei.definition != null) { - if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) || - ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) { - slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), - "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); - } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), - "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); - } - } else { - // Don't raise this if we're in an abstract profile, like Resource - if (!profile.getAbstract()) - rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), "This element is not allowed by the profile " + profile.getUrl()); - } - // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements - boolean isXmlAttr = false; - if (ei.definition != null) { - for (Enumeration r : ei.definition.getRepresentation()) { - if (r.getValue() == PropertyRepresentation.XMLATTR) { - isXmlAttr = true; - break; - } - } - } - - if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { - boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order"); - } - if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order in ordered slice"); - if (ei.definition == null || !isXmlAttr) - last = ei.index; - if (ei.slice != null) - lastSlice = ei.sliceindex; - else - lastSlice = -1; - } - return problematicPaths; - } - - public List listChildren(Element element, NodeStack stack) { - // 1. List the children, and remember their exact path (convenience) - List children = new ArrayList(); - ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element); - while (iter.next()) - children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count())); - return children; - } - - public void checkInvariants(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException { - checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); - } - - public boolean matchSlice(ValidatorHostContext hostContext, List errors, List sliceInfo, StructureDefinition profile, NodeStack stack, - ElementDefinition slicer, boolean unsupportedSlicing, List problematicPaths, int sliceOffset, int i, ElementDefinition ed, - boolean childUnsupportedSlicing, ElementInfo ei) { - boolean match = false; - if (slicer == null || slicer == ed) { - match = nameMatches(ei.getName(), tail(ed.getPath())); - } else { - if (nameMatches(ei.getName(), tail(ed.getPath()))) - try { - match = sliceMatches(hostContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack); - if (match) { - ei.slice = slicer; - - // Since a defined slice was found, this is not an additional (undefined) slice. - ei.additionalSlice = false; - } else if (ei.slice == null) { - // if the specified slice is undefined, keep track of the fact this is an additional (undefined) slice, but only if a slice wasn't found previously - ei.additionalSlice = true; - } - } catch (FHIRException e) { - rule(errors, IssueType.PROCESSING, ei.line(), ei.col(), ei.getPath(), false, e.getMessage()); - unsupportedSlicing = true; - childUnsupportedSlicing = true; - } - } - if (match) { - boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); - if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, "Profile " + profile.getUrl() + ", Element matches more than one slice - " + (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()) + ", " + (ed.hasSliceName() ? ed.getSliceName() : ""))) { - ei.definition = ed; - if (ei.slice == null) { - ei.index = i; - } else { - ei.index = sliceOffset; - ei.sliceindex = i - (sliceOffset + 1); - } - } - } else if (childUnsupportedSlicing) { - problematicPaths.add(ed.getPath()); - } - return unsupportedSlicing; - } - - private ElementDefinition getElementByTail(StructureDefinition p, String tail) throws DefinitionException { - if (tail == null) - return p.getSnapshot().getElement().get(0); - for (ElementDefinition t : p.getSnapshot().getElement()) { - if (tail.equals(t.getId())) - return t; - } - throw new DefinitionException("Unable to find element with id '" + tail + "'"); - } - - private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { - if (isBundleEntry(ei.getPath())) { - Element req = ep.getNamedChild("request"); - Element resp = ep.getNamedChild("response"); - Element fullUrl = ep.getNamedChild("fullUrl"); - Element method = null; - Element url = null; - if (req != null) { - method = req.getNamedChild("method"); - url = req.getNamedChild("url"); - } - if (resp != null) { - return IdStatus.OPTIONAL; - } - if (method == null) { - if (fullUrl == null) - return IdStatus.REQUIRED; - else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:")) - return IdStatus.OPTIONAL; - else - return IdStatus.REQUIRED; - } else { - String s = method.primitiveValue(); - if (s.equals("PUT")) { - if (url == null) - return IdStatus.REQUIRED; - else - return IdStatus.OPTIONAL; // or maybe prohibited? not clear - } else if (s.equals("POST")) - return IdStatus.OPTIONAL; // this should be prohibited, but see task 9102 - else // actually, we should never get to here; a bundle entry with method get/delete should not have a resource - return IdStatus.OPTIONAL; - } - } else if (isParametersEntry(ei.getPath()) || isBundleOutcome(ei.getPath())) - return IdStatus.OPTIONAL; - else - return IdStatus.REQUIRED; - } - - private void checkInvariants(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException { - if (noInvariantChecks) - return; - - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || profile.getUrl().equals(inv.getSource()))) { - @SuppressWarnings("unchecked") - Set invList = executionId.equals(element.getUserString(EXECUTION_ID)) ? (Set) element.getUserData(EXECUTED_CONSTRAINT_LIST) : null; - if (invList == null) { - invList = new HashSet<>(); - element.setUserData(EXECUTED_CONSTRAINT_LIST, invList); - element.setUserData(EXECUTION_ID, executionId); - } - if (!invList.contains(inv.getKey())) { - invList.add(inv.getKey()); - checkInvariant(hostContext, errors, path, profile, resource, element, inv); - } else { - //System.out.println("Skip "+inv.getKey()+" on "+path); - } - } - } - } - - public void checkInvariant(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { - ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); - if (n == null) { - long t = System.nanoTime(); - try { - n = fpe.parse(fixExpr(inv.getExpression())); - } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); - } - fpeTime = fpeTime + (System.nanoTime() - t); - inv.setUserData("validator.expression.cache", n); - } - - String msg; - boolean ok; - try { - long t = System.nanoTime(); - ok = fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), element, n); - fpeTime = fpeTime + (System.nanoTime() - t); - msg = fpe.forLog(); - } catch (Exception ex) { - ok = false; - msg = ex.getMessage(); - } - if (!ok) { - if (!Utilities.noString(msg)) - msg = " (" + msg + ")"; - 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() + "]"); - else if (bpWarnings == BestPracticeWarningLevel.Warning) - warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - else if (bpWarnings == BestPracticeWarningLevel.Error) - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { - warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } - } - } - - private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { - // first entry must be a messageheader - if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), - "The first entry in a message must be a MessageHeader")) { - List elements = messageHeader.getChildren("focus"); - for (Element elem : elements) - validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); - } - } - - private void validateObservation(List errors, Element element, NodeStack stack) { - // all observations should have a subject, a performer, and a time - - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); - List performers = new ArrayList<>(); - element.getNamedChildren("performer", performers); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, - "All observations should have an effectiveDateTime or an effectivePeriod"); - } - - /* - * The actual base entry point for internal use (re-entrant) - */ - private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { - assert stack != null; - assert resource != null; - boolean ok = true; - String resourceName = element.getType(); // todo: consider namespace...? - if (defn == null) { - long t = System.nanoTime(); - defn = element.getProperty().getStructure(); - if (defn == null) - defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); - sdTime = sdTime + (System.nanoTime() - t); - ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, "No definition found for resource type '" + resourceName + "'"); - } - - String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); - // special case: we have a bundle, and the profile is not for a bundle. We'll try the first entry instead - if (!type.equals(resourceName) && resourceName.equals("Bundle")) { - NodeStack first = getFirstEntry(stack); - if (first != null && first.getElement().getType().equals(type)) { - element = first.element; - stack = first; - resourceName = element.getType(); - idstatus = IdStatus.OPTIONAL; // why? - } - // todo: validate everything in this bundle. - } - ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), "Specified profile type was '" + type + "', but found type '" + resourceName + "'"); - - if (ok) { - if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource requires an id, but none is present"); - else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource has an id, but none is allowed"); - start(hostContext, errors, element, element, defn, stack); // root is both definition and type - } - } - - private NodeStack getFirstEntry(NodeStack bundle) { - List list = new ArrayList(); - bundle.getElement().getNamedChildren("entry", list); - if (list.isEmpty()) - return null; - Element resource = list.get(0).getNamedChild("resource"); - if (resource == null) - return null; + if (ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR)) + type = ei.getElement().getType(); else { - NodeStack entry = bundle.push(list.get(0), 0, list.get(0).getProperty().getDefinition(), list.get(0).getProperty().getDefinition()); - return entry.push(resource, -1, resource.getProperty().getDefinition(), context.fetchTypeDefinition(resource.fhirType()).getSnapshot().getElementFirstRep()); - } - } - - private void validateSections(List errors, List entries, Element focus, NodeStack stack, String fullUrl, String id) { - List sections = new ArrayList(); - focus.getNamedChildren("section", sections); - int i = 1; - for (Element section : sections) { - NodeStack localStack = stack.push(section, i, null, null); - - // technically R4+, but there won't be matches from before that - validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "author", "Section"); - validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "focus", "Section"); - - List sectionEntries = new ArrayList(); - section.getNamedChildren("entry", sectionEntries); - int j = 1; - for (Element sectionEntry : sectionEntries) { - NodeStack localStack2 = localStack.push(sectionEntry, j, null, null); - validateBundleReference(errors, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id); - j++; + prefix = prefix.substring(0, prefix.length() - 3); + for (TypeRefComponent t : ei.definition.getType()) + if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { + type = t.getWorkingCode(); + // Excluding reference is a kludge to get around versioning issues + if (t.hasProfile() && !type.equals("Reference")) + profiles.add(t.getProfile().get(0).getValue()); } - validateSections(errors, entries, section, localStack, fullUrl, id); - i++; } - } + if (type == null) { + TypeRefComponent trc = ei.definition.getType().get(0); + if (trc.getWorkingCode().equals("Reference")) + type = "Reference"; + else + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false,messages.getString("The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_"), ei.getName(), describeTypes(ei.definition.getType())); + } + } else if (ei.definition.getContentReference() != null) { + typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); + } else if (ei.definition.getType().size() == 1 && ("Element".equals(ei.definition.getType().get(0).getWorkingCode()) || "BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode()))) { + if (ei.definition.getType().get(0).hasProfile()) { + CanonicalType pu = ei.definition.getType().get(0).getProfile().get(0); + if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) + profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT)); + else + profiles.add(pu.getValue()); + } + } - private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException { - if (criteria.hasFixed()) { - List msgs = new ArrayList(); - checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); - return msgs.size() == 0; - } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { - throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + if (type != null) { + if (type.startsWith("@")) { + ei.definition = findElement(profile, type.substring(1)); + type = null; + } + } + NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); + if (debug) { + System.out.println(" " + localStack.getLiteralPath()); + } + String localStackLiterapPath = localStack.getLiteralPath(); + String eiPath = ei.getPath(); + assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; + boolean thisIsCodeableConcept = false; + String thisExtension = null; + boolean checkDisplay = true; + + checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.getElement(), localStack, true); + + ei.getElement().markValidation(profile, ei.definition); + boolean elementValidated = false; + if (type != null) { + if (isPrimitiveType(type)) { + checkPrimitive(hostContext, errors, ei.getPath(), type, ei.definition, ei.getElement(), profile, stack); } else { - throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + if (ei.definition.hasFixed()) { + checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getFixed(), profile.getUrl(), ei.definition.getSliceName(), null); + } + if (ei.definition.hasPattern()) { + checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getPattern(), profile.getUrl(), ei.definition.getSliceName(), null, true); + } + } + if (type.equals("Identifier")) { + checkIdentifier(errors, ei.getPath(), ei.getElement(), ei.definition); + } else if (type.equals("Coding")) { + checkCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack); + } else if (type.equals("CodeableConcept")) { + checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack); + thisIsCodeableConcept = true; + } else if (type.equals("Reference")) { + checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, ei.definition, actualType, localStack); + // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension + } else if (type.equals("Extension")) { + Element eurl = ei.getElement().getNamedChild("url"); + if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null,messages.getString("Extensionurl_is_required"))) { + String url = eurl.primitiveValue(); + thisExtension = url; + if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url),messages.getString("Extensionurl_is_required"))) { + if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url),messages.getString("Extensionurl_must_be_an_absolute_URL"))) { + checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); + } + } + } + } else if (type.equals("Resource")) { + validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if + elementValidated = true; + // (str.matches(".*([.,/])work\\1$")) + } else if (Utilities.isAbsoluteUrl(type)) { + StructureDefinition defn = context.fetchTypeDefinition(type); + if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) { + List txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep()); + if (txtype.contains("CodeableConcept")) { + checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack, defn); + thisIsCodeableConcept = true; + } else if (txtype.contains("Coding")) { + checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn); + } + } + } + } else { + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null,messages.getString("Unrecognised_Content_"), ei.getName())) + validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); + } + StructureDefinition p = null; + String tail = null; + if (profiles.isEmpty()) { + if (type != null) { + p = getProfileForType(type, ei.definition.getType()); + + // If dealing with a primitive type, then we need to check the current child against + // the invariants (constraints) on the current element, because otherwise it only gets + // checked against the primary type's invariants: LLoyd + //if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { + // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); + //} + + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_type_"), type); + } + } else if (profiles.size() == 1) { + String url = profiles.get(0); + if (url.contains("#")) { + tail = url.substring(url.indexOf("#") + 1); + url = url.substring(0, url.indexOf("#")); + } + p = this.context.fetchResource(StructureDefinition.class, url); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), profiles.get(0)); + } else { + elementValidated = true; + HashMap> goodProfiles = new HashMap>(); + HashMap> badProfiles = new HashMap>(); + for (String typeProfile : profiles) { + String url = typeProfile; + tail = null; + if (url.contains("#")) { + tail = url.substring(url.indexOf("#") + 1); + url = url.substring(0, url.indexOf("#")); + } + p = this.context.fetchResource(StructureDefinition.class, typeProfile); + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), typeProfile)) { + List profileErrors = new ArrayList(); + validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + if (hasErrors(profileErrors)) + badProfiles.put(typeProfile, profileErrors); + else + goodProfiles.put(typeProfile, profileErrors); + } + } + if (goodProfiles.size() == 1) { + errors.addAll(goodProfiles.values().iterator().next()); + } else if (goodProfiles.size() == 0) { + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Unable_to_find_matching_profile_among_choices_"), StringUtils.join("; ", profiles)); + for (String m : badProfiles.keySet()) { + p = this.context.fetchResource(StructureDefinition.class, m); + for (ValidationMessage message : badProfiles.get(m)) { + message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); + errors.add(message); + } + } + } else { + warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Found_multiple_matching_profiles_among_choices_"), StringUtils.join("; ", goodProfiles.keySet())); + for (String m : goodProfiles.keySet()) { + p = this.context.fetchResource(StructureDefinition.class, m); + for (ValidationMessage message : goodProfiles.get(m)) { + message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); + errors.add(message); + } + } + } + } + if (p != null) { + trackUsage(p, hostContext, element); + + if (!elementValidated) { + if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + else + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + } + 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.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + } + } + } + } + + private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { + if (tracker != null) { + tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); + } + } + + private boolean hasMapping(String url, StructureDefinition defn, ElementDefinition elem) { + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + return true; + } + } + + } + return false; + } + + private List getMapping(String url, StructureDefinition defn, ElementDefinition elem) { + List res = new ArrayList<>(); + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + res.add(m.getMap()); + } + } + } + return res; + } + + public void checkMustSupport(StructureDefinition profile, ElementInfo ei) { + String usesMustSupport = profile.getUserString("usesMustSupport"); + if (usesMustSupport == null) { + usesMustSupport = "N"; + for (ElementDefinition pe : profile.getSnapshot().getElement()) { + if (pe.getMustSupport()) { + usesMustSupport = "Y"; + break; + } + } + profile.setUserData("usesMustSupport", usesMustSupport); + } + if (usesMustSupport.equals("Y")) { + String elementSupported = ei.getElement().getUserString("elementSupported"); + if (elementSupported == null || ei.definition.getMustSupport()) + if (ei.definition.getMustSupport()) { + ei.getElement().setUserData("elementSupported", "Y"); } } + } - private boolean yearIsValid(String v) { - if (v == null) { - return false; + public void checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, + List childDefinitions, List children, List problematicPaths) throws DefinitionException { + // 3. report any definitions that have a cardinality problem + for (ElementDefinition ed : childDefinitions) { + if (ed.getRepresentation().isEmpty()) { // ignore xml attributes + int count = 0; + List slices = null; + if (ed.hasSlicing()) + slices = ProfileUtilities.getSliceList(profile, ed); + for (ElementInfo ei : children) + if (ei.definition == ed) + count++; + else if (slices != null) { + for (ElementDefinition sed : slices) { + if (ei.definition == sed) { + count++; + break; + } + } + } + String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; + if (ed.getMin() > 0) { + if (problematicPaths.contains(ed.getPath())) + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation"), location, Integer.toString(ed.getMin())); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_minimum_required___but_only_found_"), location, Integer.toString(ed.getMin()), Integer.toString(count)); } + if (ed.hasMax() && !ed.getMax().equals("*")) { + if (problematicPaths.contains(ed.getPath())) + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation"), location, ed.getMax()); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_max_allowed___but_found_"), location, ed.getMax(), Integer.toString(count)); + } + } + } + } + + public List assignChildren(ValidatorHostContext hostContext, List errors, StructureDefinition profile, Element resource, + NodeStack stack, List childDefinitions, List children) throws DefinitionException { + // 2. assign children to a definition + // for each definition, for each child, check whether it belongs in the slice + ElementDefinition slicer = null; + boolean unsupportedSlicing = false; + List problematicPaths = new ArrayList(); + String slicingPath = null; + int sliceOffset = 0; + for (int i = 0; i < childDefinitions.size(); i++) { + ElementDefinition ed = childDefinitions.get(i); + boolean childUnsupportedSlicing = false; + boolean process = true; + if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) + slicingPath = ed.getPath(); + else if (slicingPath != null && ed.getPath().equals(slicingPath)) + ; // nothing + else if (slicingPath != null && !ed.getPath().startsWith(slicingPath)) + slicingPath = null; + // where are we with slicing + if (ed.hasSlicing()) { + if (slicer != null && slicer.getPath().equals(ed.getPath())) { + String errorContext = "profile " + profile.getUrl(); + if (!resource.getChildValue("id").isEmpty()) + errorContext += "; instance " + resource.getChildValue("id"); + throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); + } + slicer = ed; + process = false; + sliceOffset = i; + } else if (slicer != null && !slicer.getPath().equals(ed.getPath())) + slicer = null; + + for (ElementInfo ei : children) { + if (ei.sliceInfo == null) { + ei.sliceInfo = new ArrayList<>(); + } + unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei); + } + } + int last = -1; + int lastSlice = -1; + for (ElementInfo ei : children) { + String sliceInfo = ""; + if (slicer != null) + sliceInfo = " (slice: " + slicer.getPath() + ")"; + if (!unsupportedSlicing) + if (ei.additionalSlice && ei.definition != null) { + if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) || + ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) { + slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), + "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)),messages.getString("This_element_does_not_match_any_known_slice_"), (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + } + } else { + // Don't raise this if we're in an abstract profile, like Resource + if (!profile.getAbstract()) + rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null),messages.getString("This_element_is_not_allowed_by_the_profile_"), profile.getUrl()); + } + // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements + boolean isXmlAttr = false; + if (ei.definition != null) { + for (Enumeration r : ei.definition.getRepresentation()) { + if (r.getValue() == PropertyRepresentation.XMLATTR) { + isXmlAttr = true; + break; + } + } + } + + if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { + boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok,messages.getString("As_specified_by_profile__Element__is_out_of_order"), profile.getUrl(), ei.getName()); + } + if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr,messages.getString("As_specified_by_profile__Element__is_out_of_order_in_ordered_slice"), profile.getUrl(), ei.getName()); + if (ei.definition == null || !isXmlAttr) + last = ei.index; + if (ei.slice != null) + lastSlice = ei.sliceindex; + else + lastSlice = -1; + } + return problematicPaths; + } + + public List listChildren(Element element, NodeStack stack) { + // 1. List the children, and remember their exact path (convenience) + List children = new ArrayList(); + ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element); + while (iter.next()) + children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count())); + return children; + } + + public void checkInvariants(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException { + checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); + } + + public boolean matchSlice(ValidatorHostContext hostContext, List errors, List sliceInfo, StructureDefinition profile, NodeStack stack, + ElementDefinition slicer, boolean unsupportedSlicing, List problematicPaths, int sliceOffset, int i, ElementDefinition ed, + boolean childUnsupportedSlicing, ElementInfo ei) { + boolean match = false; + if (slicer == null || slicer == ed) { + match = nameMatches(ei.getName(), tail(ed.getPath())); + } else { + if (nameMatches(ei.getName(), tail(ed.getPath()))) try { - int i = Integer.parseInt(v.substring(0, Math.min(4, v.length()))); - return i >= 1800 && i <= thisYear() + 80; - } catch (NumberFormatException e) { - return false; + match = sliceMatches(hostContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack); + if (match) { + ei.slice = slicer; + + // Since a defined slice was found, this is not an additional (undefined) slice. + ei.additionalSlice = false; + } else if (ei.slice == null) { + // if the specified slice is undefined, keep track of the fact this is an additional (undefined) slice, but only if a slice wasn't found previously + ei.additionalSlice = true; + } + } catch (FHIRException e) { + rule(errors, IssueType.PROCESSING, ei.line(), ei.col(), ei.getPath(), false, e.getMessage()); + unsupportedSlicing = true; + childUnsupportedSlicing = true; } } + if (match) { + boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); + if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk,messages.getString("Profile__Element_matches_more_than_one_slice___"), profile.getUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) { + ei.definition = ed; + if (ei.slice == null) { + ei.index = i; + } else { + ei.index = sliceOffset; + ei.sliceindex = i - (sliceOffset + 1); + } + } + } else if (childUnsupportedSlicing) { + problematicPaths.add(ed.getPath()); + } + return unsupportedSlicing; + } - private int thisYear() { - return Calendar.getInstance().get(Calendar.YEAR); + private ElementDefinition getElementByTail(StructureDefinition p, String tail) throws DefinitionException { + if (tail == null) + return p.getSnapshot().getElement().get(0); + for (ElementDefinition t : p.getSnapshot().getElement()) { + if (tail.equals(t.getId())) + return t; + } + throw new DefinitionException("Unable to find element with id '" + tail + "'"); + } + + private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { + if (isBundleEntry(ei.getPath())) { + Element req = ep.getNamedChild("request"); + Element resp = ep.getNamedChild("response"); + Element fullUrl = ep.getNamedChild("fullUrl"); + Element method = null; + Element url = null; + if (req != null) { + method = req.getNamedChild("method"); + url = req.getNamedChild("url"); + } + if (resp != null) { + return IdStatus.OPTIONAL; + } + if (method == null) { + if (fullUrl == null) + return IdStatus.REQUIRED; + else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:")) + return IdStatus.OPTIONAL; + else + return IdStatus.REQUIRED; + } else { + String s = method.primitiveValue(); + if (s.equals("PUT")) { + if (url == null) + return IdStatus.REQUIRED; + else + return IdStatus.OPTIONAL; // or maybe prohibited? not clear + } else if (s.equals("POST")) + return IdStatus.OPTIONAL; // this should be prohibited, but see task 9102 + else // actually, we should never get to here; a bundle entry with method get/delete should not have a resource + return IdStatus.OPTIONAL; + } + } else if (isParametersEntry(ei.getPath()) || isBundleOutcome(ei.getPath())) + return IdStatus.OPTIONAL; + else + return IdStatus.REQUIRED; + } + + private void checkInvariants(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException { + if (noInvariantChecks) + return; + + for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { + if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || profile.getUrl().equals(inv.getSource()))) { + @SuppressWarnings("unchecked") + Set invList = executionId.equals(element.getUserString(EXECUTION_ID)) ? (Set) element.getUserData(EXECUTED_CONSTRAINT_LIST) : null; + if (invList == null) { + invList = new HashSet<>(); + element.setUserData(EXECUTED_CONSTRAINT_LIST, invList); + element.setUserData(EXECUTION_ID, executionId); + } + if (!invList.contains(inv.getKey())) { + invList.add(inv.getKey()); + checkInvariant(hostContext, errors, path, profile, resource, element, inv); + } else { + //System.out.println("Skip "+inv.getKey()+" on "+path); + } + } + } + } + + public void checkInvariant(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { + ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); + if (n == null) { + long t = System.nanoTime(); + try { + n = fpe.parse(fixExpr(inv.getExpression())); + } catch (FHIRLexerException e) { + throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + } + fpeTime = fpeTime + (System.nanoTime() - t); + inv.setUserData("validator.expression.cache", n); } - public class NodeStack { - private ElementDefinition definition; - private Element element; - private ElementDefinition extension; - private String literalPath; // xpath format - private List logicalPaths; // dotted format, various entry points - private NodeStack parent; - private ElementDefinition type; - private String workingLang; + String msg; + boolean ok; + try { + long t = System.nanoTime(); + ok = fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), element, n); + fpeTime = fpeTime + (System.nanoTime() - t); + msg = fpe.forLog(); + } catch (Exception ex) { + ok = false; + msg = ex.getMessage(); + } + if (!ok) { + if (!Utilities.noString(msg)) + msg = " (" + msg + ")"; + 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() + "]"); + else if (bpWarnings == BestPracticeWarningLevel.Warning) + warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + else if (bpWarnings == BestPracticeWarningLevel.Error) + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { + warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } + } + } - public NodeStack() { + private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { + // first entry must be a messageheader + if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"),messages.getString("The_first_entry_in_a_message_must_be_a_MessageHeader"))) { + List elements = messageHeader.getChildren("focus"); + for (Element elem : elements) + validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); + } + } + + private void validateObservation(List errors, Element element, NodeStack stack) { + // all observations should have a subject, a performer, and a time + + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); + List performers = new ArrayList<>(); + element.getNamedChildren("performer", performers); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, + "All observations should have an effectiveDateTime or an effectivePeriod"); + } + + /* + * The actual base entry point for internal use (re-entrant) + */ + private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { + assert stack != null; + assert resource != null; + boolean ok = true; + String resourceName = element.getType(); // todo: consider namespace...? + if (defn == null) { + long t = System.nanoTime(); + defn = element.getProperty().getStructure(); + if (defn == null) + defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); + sdTime = sdTime + (System.nanoTime() - t); + ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null,messages.getString("No_definition_found_for_resource_type_"), resourceName); + } + + String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); + // special case: we have a bundle, and the profile is not for a bundle. We'll try the first entry instead + if (!type.equals(resourceName) && resourceName.equals("Bundle")) { + NodeStack first = getFirstEntry(stack); + if (first != null && first.getElement().getType().equals(type)) { + element = first.element; + stack = first; + resourceName = element.getType(); + idstatus = IdStatus.OPTIONAL; // why? + } + // todo: validate everything in this bundle. + } + ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName),messages.getString("Specified_profile_type_was__but_found_type_"), type, resourceName); + + if (ok) { + if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_requires_an_id_but_none_is_present")); + else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_an_id_but_none_is_allowed")); + start(hostContext, errors, element, element, defn, stack); // root is both definition and type + } + } + + private NodeStack getFirstEntry(NodeStack bundle) { + List list = new ArrayList(); + bundle.getElement().getNamedChildren("entry", list); + if (list.isEmpty()) + return null; + Element resource = list.get(0).getNamedChild("resource"); + if (resource == null) + return null; + else { + NodeStack entry = bundle.push(list.get(0), 0, list.get(0).getProperty().getDefinition(), list.get(0).getProperty().getDefinition()); + return entry.push(resource, -1, resource.getProperty().getDefinition(), context.fetchTypeDefinition(resource.fhirType()).getSnapshot().getElementFirstRep()); + } + } + + private void validateSections(List errors, List entries, Element focus, NodeStack stack, String fullUrl, String id) { + List sections = new ArrayList(); + focus.getNamedChildren("section", sections); + int i = 1; + for (Element section : sections) { + NodeStack localStack = stack.push(section, i, null, null); + + // technically R4+, but there won't be matches from before that + validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "author", "Section"); + validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "focus", "Section"); + + List sectionEntries = new ArrayList(); + section.getNamedChildren("entry", sectionEntries); + int j = 1; + for (Element sectionEntry : sectionEntries) { + NodeStack localStack2 = localStack.push(sectionEntry, j, null, null); + validateBundleReference(errors, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id); + j++; + } + validateSections(errors, entries, section, localStack, fullUrl, id); + i++; + } + } + + private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException { + if (criteria.hasFixed()) { + List msgs = new ArrayList(); + checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); + return msgs.size() == 0; + } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { + throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + } else { + throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + } + } + + private boolean yearIsValid(String v) { + if (v == null) { + return false; + } + try { + int i = Integer.parseInt(v.substring(0, Math.min(4, v.length()))); + return i >= 1800 && i <= thisYear() + 80; + } catch (NumberFormatException e) { + return false; + } + } + + private int thisYear() { + return Calendar.getInstance().get(Calendar.YEAR); + } + + public class NodeStack { + private ElementDefinition definition; + private Element element; + private ElementDefinition extension; + private String literalPath; // xpath format + private List logicalPaths; // dotted format, various entry points + private NodeStack parent; + private ElementDefinition type; + private String workingLang; + + public NodeStack() { + } + + public NodeStack(Element element) { + this.element = element; + literalPath = element.getName(); + workingLang = validationLanguage; + if (!element.getName().equals(element.fhirType())) { + logicalPaths = new ArrayList<>(); + logicalPaths.add(element.fhirType()); + } + } + + public NodeStack(Element element, String refPath) { + this.element = element; + literalPath = refPath + "->" + element.getName(); + workingLang = validationLanguage; + } + + public String addToLiteralPath(String... path) { + StringBuilder b = new StringBuilder(); + b.append(getLiteralPath()); + for (String p : path) { + if (p.startsWith(":")) { + b.append("["); + b.append(p.substring(1)); + b.append("]"); + } else { + b.append("."); + b.append(p); } + } + return b.toString(); + } - public NodeStack(Element element) { - this.element = element; - literalPath = element.getName(); - workingLang = validationLanguage; - if (!element.getName().equals(element.fhirType())) { - logicalPaths = new ArrayList<>(); - logicalPaths.add(element.fhirType()); + private ElementDefinition getDefinition() { + return definition; + } + + private Element getElement() { + return element; + } + + protected String getLiteralPath() { + return literalPath == null ? "" : literalPath; + } + + private List getLogicalPaths() { + return logicalPaths == null ? new ArrayList() : logicalPaths; + } + + private ElementDefinition getType() { + return type; + } + + private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) { + return pushInternal(element, count, definition, type, "->"); + } + + private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) { + return pushInternal(element, count, definition, type, "."); + } + + private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) { + NodeStack res = new NodeStack(); + res.parent = this; + res.workingLang = this.workingLang; + res.element = element; + res.definition = definition; + res.literalPath = getLiteralPath() + sep + element.getName(); + if (count > -1) + res.literalPath = res.literalPath + "[" + Integer.toString(count) + "]"; + else if (element.getSpecial() == null && element.getProperty().isList()) + res.literalPath = res.literalPath + "[0]"; + else if (element.getProperty().isChoice()) { + String n = res.literalPath.substring(res.literalPath.lastIndexOf(".") + 1); + String en = element.getProperty().getName(); + en = en.substring(0, en.length() - 3); + String t = n.substring(en.length()); + if (isPrimitiveType(Utilities.uncapitalize(t))) + t = Utilities.uncapitalize(t); + res.literalPath = res.literalPath.substring(0, res.literalPath.lastIndexOf(".")) + "." + en + ".ofType(" + t + ")"; + } + res.logicalPaths = new ArrayList(); + if (type != null) { + // type will be bull if we on a stitching point of a contained resource, or if.... + res.type = type; + String tn = res.type.getPath(); + String t = tail(definition.getPath()); + if ("Resource".equals(tn)) { + tn = element.fhirType(); + } + for (String lp : getLogicalPaths()) { + res.logicalPaths.add(lp + "." + t); + if (t.endsWith("[x]")) + res.logicalPaths.add(lp + "." + t.substring(0, t.length() - 3) + type.getPath()); + } + res.logicalPaths.add(tn); + } else if (definition != null) { + for (String lp : getLogicalPaths()) { + res.logicalPaths.add(lp + "." + element.getName()); + } + res.logicalPaths.add(definition.typeSummary()); + } else + res.logicalPaths.addAll(getLogicalPaths()); + return res; + } + + private void setType(ElementDefinition type) { + this.type = type; + } + } + + public String reportTimes() { + String s = String.format("Times (ms): overall = %d, tx = %d, sd = %d, load = %d, fpe = %d", overall / 1000000, txTime / 1000000, sdTime / 1000000, loadTime / 1000000, fpeTime / 1000000); + overall = 0; + txTime = 0; + sdTime = 0; + loadTime = 0; + fpeTime = 0; + return s; + } + + public boolean isNoBindingMsgSuppressed() { + return noBindingMsgSuppressed; + } + + public IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) { + this.noBindingMsgSuppressed = noBindingMsgSuppressed; + return this; + } + + + public boolean isNoTerminologyChecks() { + return noTerminologyChecks; + } + + public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks) { + this.noTerminologyChecks = noTerminologyChecks; + return this; + } + + public void checkAllInvariants() { + for (StructureDefinition sd : context.allStructures()) { + if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { + if (inv.hasExpression()) { + try { + ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); + if (n == null) { + n = fpe.parse(fixExpr(inv.getExpression())); + inv.setUserData("validator.expression.cache", n); + } + fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); + } catch (Exception e) { + System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); + } } + } } + } + } + } - public NodeStack(Element element, String refPath) { - this.element = element; - literalPath = refPath + "->" + element.getName(); - workingLang = validationLanguage; - } - - public String addToLiteralPath(String... path) { - StringBuilder b = new StringBuilder(); - b.append(getLiteralPath()); - for (String p : path) { - if (p.startsWith(":")) { - b.append("["); - b.append(p.substring(1)); - b.append("]"); - } else { - b.append("."); - b.append(p); - } - } - return b.toString(); - } - - private ElementDefinition getDefinition() { - return definition; - } - - private Element getElement() { - return element; - } - - protected String getLiteralPath() { - return literalPath == null ? "" : literalPath; - } - - private List getLogicalPaths() { - return logicalPaths == null ? new ArrayList() : logicalPaths; - } - - private ElementDefinition getType() { - return type; - } - - private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) { - return pushInternal(element, count, definition, type, "->"); - } - - private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) { - return pushInternal(element, count, definition, type, "."); - } - - private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) { - NodeStack res = new NodeStack(); - res.parent = this; - res.workingLang = this.workingLang; - res.element = element; - res.definition = definition; - res.literalPath = getLiteralPath() + sep + element.getName(); - if (count > -1) - res.literalPath = res.literalPath + "[" + Integer.toString(count) + "]"; - else if (element.getSpecial() == null && element.getProperty().isList()) - res.literalPath = res.literalPath + "[0]"; - else if (element.getProperty().isChoice()) { - String n = res.literalPath.substring(res.literalPath.lastIndexOf(".") + 1); - String en = element.getProperty().getName(); - en = en.substring(0, en.length() - 3); - String t = n.substring(en.length()); - if (isPrimitiveType(Utilities.uncapitalize(t))) - t = Utilities.uncapitalize(t); - res.literalPath = res.literalPath.substring(0, res.literalPath.lastIndexOf(".")) + "." + en + ".ofType(" + t + ")"; - } - res.logicalPaths = new ArrayList(); - if (type != null) { - // type will be bull if we on a stitching point of a contained resource, or if.... - res.type = type; - String tn = res.type.getPath(); - String t = tail(definition.getPath()); - if ("Resource".equals(tn)) { - tn = element.fhirType(); - } - for (String lp : getLogicalPaths()) { - res.logicalPaths.add(lp + "." + t); - if (t.endsWith("[x]")) - res.logicalPaths.add(lp + "." + t.substring(0, t.length() - 3) + type.getPath()); - } - res.logicalPaths.add(tn); - } else if (definition != null) { - for (String lp : getLogicalPaths()) { - res.logicalPaths.add(lp + "." + element.getName()); - } - res.logicalPaths.add(definition.typeSummary()); - } else - res.logicalPaths.addAll(getLogicalPaths()); - return res; - } - - private void setType(ElementDefinition type) { - this.type = type; - } + private String fixExpr(String expr) { + // this is a hack work around for past publication of wrong FHIRPath expressions + // R4 + // waiting for 4.0.2 + if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) { + return "probablility.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))"; } - public String reportTimes() { - String s = String.format("Times (ms): overall = %d, tx = %d, sd = %d, load = %d, fpe = %d", overall / 1000000, txTime / 1000000, sdTime / 1000000, loadTime / 1000000, fpeTime / 1000000); - overall = 0; - txTime = 0; - sdTime = 0; - loadTime = 0; - fpeTime = 0; - return s; - } + // handled in 4.0.1 + if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) + return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())"; + if ("isModifier implies isModifierReason.exists()".equals(expr)) + return "(isModifier.exists() and isModifier) implies isModifierReason.exists()"; + if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) + return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))"; + if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) + return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()"; + if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) + return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()"; - public boolean isNoBindingMsgSuppressed() { - return noBindingMsgSuppressed; - } + // R3 + if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')"; + if ("value.empty() or code!=component.code".equals(expr)) + return "value.empty() or (code in component.code).not()"; + if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; + if ("element.all(definition and min and max)".equals(expr)) + return "element.all(definition.exists() and min.exists() and max.exists())"; + if ("telecom or endpoint".equals(expr)) + return "telecom.exists() or endpoint.exists()"; + if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; + if ("searchType implies type = 'string'".equals(expr)) + return "searchType.exists() implies type = 'string'"; + if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) + return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')"; + if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) + return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; - public IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) { - this.noBindingMsgSuppressed = noBindingMsgSuppressed; - return this; - } + if ("".equals(expr)) + return ""; + return expr; + } + public IEvaluationContext getExternalHostServices() { + return externalHostServices; + } - public boolean isNoTerminologyChecks() { - return noTerminologyChecks; - } + public String getValidationLanguage() { + return validationLanguage; + } - public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks) { - this.noTerminologyChecks = noTerminologyChecks; - return this; - } + public void setValidationLanguage(String validationLanguage) { + this.validationLanguage = validationLanguage; + } - public void checkAllInvariants() { - for (StructureDefinition sd : context.allStructures()) { - if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition ed : sd.getSnapshot().getElement()) { - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasExpression()) { - try { - ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); - if (n == null) { - n = fpe.parse(fixExpr(inv.getExpression())); - inv.setUserData("validator.expression.cache", n); - } - fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); - } catch (Exception e) { - System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); - } - } - } - } - } - } - } + public boolean isDebug() { + return debug; + } - private String fixExpr(String expr) { - // this is a hack work around for past publication of wrong FHIRPath expressions - // R4 - // waiting for 4.0.2 - if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) { - return "probablility.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))"; - } - - // handled in 4.0.1 - if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) - return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())"; - if ("isModifier implies isModifierReason.exists()".equals(expr)) - return "(isModifier.exists() and isModifier) implies isModifierReason.exists()"; - if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) - return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))"; - if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) - return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()"; - if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) - return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()"; - - // R3 - if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')"; - if ("value.empty() or code!=component.code".equals(expr)) - return "value.empty() or (code in component.code).not()"; - if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; - if ("element.all(definition and min and max)".equals(expr)) - return "element.all(definition.exists() and min.exists() and max.exists())"; - if ("telecom or endpoint".equals(expr)) - return "telecom.exists() or endpoint.exists()"; - if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; - if ("searchType implies type = 'string'".equals(expr)) - return "searchType.exists() implies type = 'string'"; - if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) - return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')"; - if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) - return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; - - if ("".equals(expr)) - return ""; - return expr; - } - - public IEvaluationContext getExternalHostServices() { - return externalHostServices; - } - - public String getValidationLanguage() { - return validationLanguage; - } - - public void setValidationLanguage(String validationLanguage) { - this.validationLanguage = validationLanguage; - } - - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } + public void setDebug(boolean debug) { + this.debug = debug; + } } From 95e02043990bfecea07327240b82965fac95ec34 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 18:11:06 +0100 Subject: [PATCH 06/52] added missin quotation mark --- .../instancevalidator/InstanceValidator.java | 643 ++++++++++-------- 1 file changed, 348 insertions(+), 295 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index 490c4d592..bf4e7eced 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -32,9 +32,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; @@ -371,8 +369,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private ValidatorHostServices validatorServices; private boolean assumeValidRestReferences; private boolean allowExamples; - private ResourceBundle messages = - ResourceBundle.getBundle("Messages", Locale.US); public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { super(); @@ -697,7 +693,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkElementUsage(List errors, Element element, NodeStack stack) { String elementUsage = element.getUserString("elementSupported"); - hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"),messages.getString("The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile"), element.getName(), element.getProperty().getStructure().getUrl()); + hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), + "The element " + element.getName() + " is not marked as 'mustSupport' in the profile " + element.getProperty().getStructure().getUrl() + ". Consider not using the element, or marking the element as must-Support in the profile"); if (element.hasChildren()) { String prevName = ""; @@ -728,7 +725,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List lines = new ArrayList(); focus.getNamedChildren("line", lines); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(),messages.getString("Expected__but_found__line_elements"), Integer.toString(fixed.getLine().size()), Integer.toString(lines.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(), + "Expected " + Integer.toString(fixed.getLine().size()) + " but found " + Integer.toString(lines.size()) + " line elements")) { for (int i = 0; i < lines.size(); i++) checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); } @@ -776,25 +774,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; // we don't validate these else { CodeSystem cs = getCodeSystem(system); - if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null,messages.getString("Unknown_Code_System_"), system)) { + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System '" + system + "'")) { ConceptDefinitionComponent def = getCodeDefinition(cs, code); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null,messages.getString("Unknown_Code_"), system, code)) - return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()),messages.getString("Display_should_be_"), def.getDisplay()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")")) + return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'"); } return false; } } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { return true; // no checks in this case } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI_"), system); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system); return false; } else { try { if (context.fetchResourceWithException(ValueSet.class, system) != null) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system"), system); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system + " - cannot use a value set URI as a system"); // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. } - hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false,messages.getString("Code_System_URI__is_unknown_so_the_code_cannot_be_validated"), system); + hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated"); return true; } catch (Exception e) { return true; @@ -826,7 +824,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List codings = new ArrayList(); focus.getNamedChildren("coding", codings); if (pattern) { - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() >= fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + 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; @@ -860,7 +860,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else { - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + 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), fixedSource, "coding", focus); } @@ -871,20 +873,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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,messages.getString("Binding_for__missing_cc"), path)) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing (cc)")) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { try { CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+ ")"); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); } } else { long t = System.nanoTime(); @@ -909,15 +911,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); 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"), cc, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); } } } else { @@ -927,10 +929,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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"), cc, stack); if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); } } } @@ -963,13 +965,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); } } } @@ -980,20 +982,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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,messages.getString("Binding_for__missing_cc"), path)) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing (cc)")) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { try { CodeableConcept cc = convertToCodeableConcept(element, logical); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_is_required_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); } } else { long t = System.nanoTime(); @@ -1018,15 +1020,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); 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"), cc, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); } } } else { @@ -1036,10 +1038,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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"), cc, stack); if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); } } } @@ -1059,7 +1061,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); if (!vr.isOk()) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Code_0_is_not_a_valid_code_in_code_system_1"), nextCode, nextSystem); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); } } } @@ -1068,7 +1070,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); } // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { @@ -1076,9 +1078,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); } } } @@ -1090,18 +1092,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String code = c.getCode(); String system = c.getSystem(); String display = c.getDisplay(); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); if (system != null && code != null && !noTerminologyChecks) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { try { long t = System.nanoTime(); ValidationResult vr = null; @@ -1111,18 +1113,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); 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"), c, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); } } } else if (binding.getStrength() == BindingStrength.REQUIRED) @@ -1131,26 +1133,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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"), c, stack); else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); } } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); } } } } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); } } } @@ -1229,19 +1231,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided could be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); else txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); } } } @@ -1249,19 +1251,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); else txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (code = " + c.getSystem() + "#" + c.getCode() + ")"); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); } } } @@ -1269,19 +1271,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); else txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); } } } @@ -1305,18 +1307,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String code = element.getNamedChildValue("code"); String system = element.getNamedChildValue("system"); String display = element.getNamedChildValue("display"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); if (system != null && code != null && !noTerminologyChecks) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { try { Coding c = ObjectConverter.readAsCoding(element); long t = System.nanoTime(); @@ -1327,18 +1329,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); 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"), c, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); } } } else if (binding.getStrength() == BindingStrength.REQUIRED) @@ -1347,26 +1349,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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"), c, stack); else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code. " + getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set. " + getErrorMessage(vr.getMessage())); } } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); } } } } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); } } } @@ -1402,13 +1404,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xverManager.matchingUrl(url)) { switch (xverManager.status(url)) { case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_invalidVersion_"), url, xverManager.getVersion(url)); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion \"" + xverManager.getVersion(url) + "\")"); break; case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_unknown_Element_id_"), url, xverManager.getElementId(url)); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id \"" + xverManager.getElementId(url) + "\")"); break; case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_Element_id__is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions"), url, xverManager.getElementId(url)); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id \"" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); break; case Valid: ex = xverManager.makeDefinition(url); @@ -1416,40 +1418,44 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat context.cacheResource(ex); break; default: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__evaluation_state_illegal"), url); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' evaluation state illegal"); break; } } else if (extensionUrl != null && !isAbsolute(url)) { if (extensionUrl.equals(profile.getUrl())) { - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url),messages.getString("Subextension_url__is_not_defined_by_the_Extension_"), url, profile.getUrl()); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), "Sub-extension url '" + url + "' is not defined by the Extension " + profile.getUrl()); } - } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url),messages.getString("The_extension__is_unknown_and_not_allowed_here"), url)) { - hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url),messages.getString("Unknown_extension_"), url); + } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), "The extension " + url + " is unknown, and not allowed here")) { + hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), "Unknown extension " + url); } } if (ex != null) { trackUsage(ex, hostContext, element); if (def.getIsModifier()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not")); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), + "Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not"); } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is")); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), + "Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is"); } // two questions // 1. can this extension be used here? checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); if (isModifier) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_be_used_as_a_modifierExtension"), url); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), + "The Extension '" + url + "' must be used as a modifierExtension"); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension"), url); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), + "The Extension '" + url + "' must not be used as an extension (it's a modifierExtension)"); // check the type of the extension: Set allowedTypes = listExtensionTypes(ex); String actualType = getExtensionType(element); if (actualType == null) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(),messages.getString("The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions"), url); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions"); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType),messages.getString("The_Extension__definition_allows_for_the_types__but_found_type_"), url, allowedTypes.toString(), actualType); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, 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, true, url); @@ -1574,13 +1580,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!ok) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_"), extUrl, contexts.toString(), plist.toString()); + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, "The extension " + extUrl + " is not allowed to be used at this point (allowed = " + contexts.toString() + "; this element is [" + plist.toString() + ")"); return false; } else { if (definition.hasContextInvariant()) { for (StringType s : definition.getContextInvariant()) { if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_"), extUrl, s.getValue()); + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, + "The extension " + extUrl + " is not allowed to be used at this point (based on context invariant '" + s.getValue() + "')"); return false; } } @@ -1629,39 +1636,53 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if ((fixed == null || fixed.isEmpty()) && focus == null) { ; // this is all good } else if ((fixed == null || fixed.isEmpty()) && focus != null) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern,messages.getString("The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile"), focus.getName(), (pattern ? "pattern" : "fixed value")); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern, "The element " + focus.getName() + " is present in the instance but not allowed in the applicable " + (pattern ? "pattern" : "fixed value") + " specified in profile"); } else if (fixed != null && !fixed.isEmpty() && focus == null) { - rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false,messages.getString("Missing_element___required_by_fixed_value_assigned_in_profile_"), propName, fixedSource); + rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false, "Missing element '" + propName + "' - required by fixed value assigned in profile " + fixedSource); } else { String value = focus.primitiveValue(); if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.CodeType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.CodeType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.StringType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.StringType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.StringType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.UriType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UriType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UriType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.DateType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.OidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.OidType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.OidType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UuidType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UuidType) fixed).getValue() + "'"); else if (fixed instanceof org.hl7.fhir.r5.model.IdType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IdType) fixed).getValue()); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value), + "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IdType) fixed).getValue() + "'"); else if (fixed instanceof Quantity) checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); else if (fixed instanceof Address) @@ -1690,15 +1711,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); else - rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false,messages.getString("Unhandled_fixed_value_type_"), fixed.getClass().getName()); + rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false, "Unhandled fixed value type " + fixed.getClass().getName()); List extensions = new ArrayList(); focus.getNamedChildren("extension", extensions); if (fixed.getExtension().size() == 0) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0,messages.getString("No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions")); - } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(),messages.getString("Extensions_count_mismatch_expected__but_found_"), Integer.toString(fixed.getExtension().size()), Integer.toString(extensions.size()))) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0, "No extensions allowed, as the specified fixed value doesn't contain any extensions"); + } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(), + "Extensions count mismatch: expected " + Integer.toString(fixed.getExtension().size()) + " but found " + Integer.toString(extensions.size()))) { for (Extension e : fixed.getExtension()) { Element ex = getExtensionByUrl(extensions, e.getUrl()); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null,messages.getString("Extension_count_mismatch_unable_to_find_extension_"), e.getUrl())) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, "Extension count mismatch: unable to find extension: " + e.getUrl())) { checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); } } @@ -1713,22 +1735,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List parts = new ArrayList(); focus.getNamedChildren("family", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(),messages.getString("Expected__but_found__family_elements"), (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), + "Expected " + (fixed.hasFamily() ? "1" : "0") + " but found " + Integer.toString(parts.size()) + " family elements")) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); } focus.getNamedChildren("given", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(),messages.getString("Expected__but_found__given_elements"), Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), + "Expected " + Integer.toString(fixed.getGiven().size()) + " but found " + Integer.toString(parts.size()) + " given elements")) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); } focus.getNamedChildren("prefix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(),messages.getString("Expected__but_found__prefix_elements"), Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), + "Expected " + Integer.toString(fixed.getPrefix().size()) + " but found " + Integer.toString(parts.size()) + " prefix elements")) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); } focus.getNamedChildren("suffix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(),messages.getString("Expected__but_found__suffix_elements"), Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), + "Expected " + Integer.toString(fixed.getSuffix().size()) + " but found " + Integer.toString(parts.size()) + " suffix elements")) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); } @@ -1736,7 +1762,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { String system = element.getNamedChildValue("system"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Identifiersystem_must_be_an_absolute_reference_not_a_local_reference")); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Identifier.system must be an absolute reference, not a local reference"); } private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { @@ -1756,38 +1782,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { if (isBlank(e.primitiveValue())) { if (e.primitiveValue() == null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_or_must_have_child_extensions")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions"); else if (e.primitiveValue().length() == 0) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_that_is_not_empty")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value that is not empty"); else if (StringUtils.isWhitespace(e.primitiveValue())) - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_should_not_only_be_whitespace")); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types should not only be whitespace"); return; } String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); if (regex != null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex),messages.getString("Element_value__does_not_meet_regex_"), e.primitiveValue(), regex); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), "Element value '" + e.primitiveValue() + "' does not meet regex '" + regex + "'"); if (type.equals("boolean")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()),messages.getString("boolean_values_must_be_true_or_false")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'"); } if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { String url = e.primitiveValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"),messages.getString("URI_values_cannot_start_with_oid")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"),messages.getString("URI_values_cannot_start_with_uuid")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) // work around an old invalid example in a core package || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url),messages.getString("URI_values_cannot_have_whitespace"), url); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), "URI values cannot start with oid:"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), "URI values cannot start with uuid:"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) + // work around an old invalid example in a core package + || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), "URI values cannot have whitespace('" + url + "')"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); if (type.equals("oid")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"),messages.getString("OIDs_must_start_with_urnoid"))) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)),messages.getString("OIDs_must_be_valid")); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), "OIDs must start with urn:oid:")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), "OIDs must be valid"); } if (type.equals("uuid")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"),messages.getString("UUIDs_must_start_with_urnuuid")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:"); try { UUID.fromString(url.substring(8)); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("UUIDs_must_be_valid_"), ex.getMessage()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid (" + ex.getMessage() + ")"); } } @@ -1799,50 +1827,57 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } catch (IOException e1) { found = false; } - rule(errors, IssueType.INVALID, e.line(), e.col(), path, found,messages.getString("URL_value__does_not_resolve"), url); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '" + url + "' does not resolve"); } } if (type.equals("id")) { // work around an old issue with ElementDefinition.id if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()),messages.getString("id_value__is_not_valid"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), "id value '" + e.primitiveValue() + "' is not valid"); } } if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0,messages.getString("value_cannot_be_empty"))) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()),messages.getString("value_should_not_start_or_finish_with_whitespace")); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576,messages.getString("value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes"))) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, "@value cannot be empty")) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), "value should not start or finish with whitespace"); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, "value is longer than permitted maximum length of 1 MB (1048576 bytes)")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); } } } if (type.equals("dateTime")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"),messages.getString("Not_a_valid_date_time")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()),messages.getString("if_a_date_has_a_time_it_must_have_a_timezone")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue() + .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), + "Not a valid date time"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), "if a date has a time, it must have a timezone"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); try { DateTimeType dt = new DateTimeType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_datetime_"), ex.getMessage()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date/time (" + ex.getMessage() + ")"); } } if (type.equals("time")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"),messages.getString("Not_a_valid_time")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue() + .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"), + "Not a valid time"); try { TimeType dt = new TimeType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_time_"), ex.getMessage()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid time (" + ex.getMessage() + ")"); } } if (type.equals("date")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"),messages.getString("Not_a_valid_date")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_value_of_"), context.getMaxLength()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), + "Not a valid date"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum value of " + context.getMaxLength()); try { DateType dt = new DateType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_date_"), ex.getMessage()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date (" + ex.getMessage() + ")"); } } if (type.equals("base64Binary")) { @@ -1871,54 +1906,56 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (charCount > 0 && charCount % 4 != 0) { String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("The_value_0_is_not_a_valid_Base64_value"), value); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value \"{0}\" is not a valid Base64 value", value); } } } if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer"), e.primitiveValue())) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer")) { Integer v = new Integer(e.getValue()).intValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); } } if (type.equals("integer64")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer64"), e.primitiveValue())) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer64")) { Long v = new Long(e.getValue()).longValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); } } if (type.equals("decimal")) { if (e.primitiveValue() != null) { DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE,messages.getString("The_value__is_not_a_valid_decimal"), e.primitiveValue())) - warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE,messages.getString("The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals"), e.primitiveValue()); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is not a valid decimal")) + warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is outside the range of commonly/reasonably supported decimals"); } } if (type.equals("instant")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"),messages.getString("The_instant__is_not_valid_by_regex"), e.primitiveValue()); - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), + "The instant '" + e.primitiveValue() + "' is not valid (by regex)"); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); try { InstantType dt = new InstantType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_instant_"), ex.getMessage()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid instant (" + ex.getMessage() + ")"); } } if (type.equals("code") && e.primitiveValue() != null) { // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace // other than single spaces in the contents - rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()),messages.getString("The_code__is_not_valid_whitespace_rules"), e.primitiveValue()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()), "The code '" + e.primitiveValue() + "' is not valid (whitespace rules)"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); } if (context.hasBinding() && e.primitiveValue() != null) { @@ -1930,10 +1967,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xhtml != null) { // if it is null, this is an error already noted in the parsers // check that the namespace is there and correct. String ns = xhtml.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); // check that inner namespaces are all correct checkInnerNS(errors, e, path, xhtml.getChildNodes()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()),messages.getString("Wrong_name_on_the_XHTML___must_start_with_div"), ns); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), "Wrong name on the XHTML ('" + ns + "') - must start with div"); // check that no illegal elements and attributes have been used checkInnerNames(errors, e, path, xhtml.getChildNodes()); } @@ -1957,7 +1994,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkInnerNames(List errors, Element e, String path, List list) { for (XhtmlNode node : list) { if (node.getNodeType() == NodeType.Element) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", "code", "samp", "img", "map", "area" ),messages.getString("Illegal_element_name_in_the_XHTML_"), node.getName()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), + "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", + "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", + "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", + "code", "samp", "img", "map", "area" + + ), "Illegal element name in the XHTML ('" + node.getName() + "')"); for (String an : node.getAttributes().keySet()) { boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", @@ -1971,7 +2014,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" ); if (!ok) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Illegal_attribute_name_in_the_XHTML__on_"), an, node.getName()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Illegal attribute name in the XHTML ('" + an + "' on '" + node.getName() + "')"); } checkInnerNames(errors, e, path, node.getChildNodes()); } @@ -1982,7 +2025,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (XhtmlNode node : list) { if (node.getNodeType() == NodeType.Element) { String ns = node.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); checkInnerNS(errors, e, path, node.getChildNodes()); } } @@ -2003,7 +2046,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ElementDefinitionBindingComponent binding = elementContext.getBinding(); if (binding.hasValueSet()) { ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null,messages.getString("ValueSet_0_not_found_by_validator"), describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, "ValueSet {0} not found by validator", describeReference(binding.getValueSet()))) { long t = System.nanoTime(); ValidationResult vr = null; if (binding.getStrength() != BindingStrength.EXAMPLE) { @@ -2012,23 +2055,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server"), value); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') could not be validated in the absence of a terminology server"); else if (binding.getStrength() == BindingStrength.REQUIRED) txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)" + getErrorMessage(vr.getMessage())); 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) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)" + getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is recommended to come from this value set)" + getErrorMessage(vr.getMessage())); } } } } } else if (!noBindingMsgSuppressed) - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"),messages.getString("Binding_has_no_source_so_cant_be_checked")); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), "Binding has no source, so can't be checked"); } private void checkQuantity(List errors, String path, Element focus, Quantity fixed, String fixedSource, boolean pattern) { @@ -2058,7 +2101,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String ref = reference.getReference(); if (Utilities.noString(ref)) { if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")),messages.getString("A_Reference_without_an_actual_reference_or_identifier_should_have_a_display")); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display"); } return; } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { @@ -2101,7 +2144,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat we = ext == null ? null : makeExternalRef(ext, path); } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS),messages.getString("Unable_to_resolve_resource_"), ref); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), "Unable to resolve resource '" + ref + "'"); } String ft; @@ -2123,15 +2166,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat break; } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource,messages.getString("The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_"), reference.getType(), container.getType("Reference").getTargetProfile()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, + "The type '" + reference.getType() + "' is not a valid Target for this element (must be one of " + container.getType("Reference").getTargetProfile() + ")"); } // the type has to match the actual - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()),messages.getString("The_specified_type__does_not_match_the_found_type_"), reference.getType(), ft); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), "The specified type '" + reference.getType() + "' does not match the found type '" + ft + "'"); } if (we != null && pol.checkType()) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null,messages.getString("Unable_to_determine_type_of_target_resource"))) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, "Unable to determine type of target resource")) { // we validate as much as we can. First, can we infer a type from the profile? boolean ok = false; TypeRefComponent type = getReferenceTypeRef(container.getType()); @@ -2140,7 +2184,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List profiles = new ArrayList<>(); for (UriType u : type.getTargetProfile()) { StructureDefinition sd = resolveProfile(profile, u.getValue()); - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null,messages.getString("Unable_to_resolve_the_profile_reference_"), u.getValue())) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, "Unable to resolve the profile reference '" + u.getValue() + "'")) { types.add(sd.getType()); if (ft.equals(sd.getType())) { ok = true; @@ -2149,7 +2193,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!pol.checkValid()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0,messages.getString("Unable_to_find_matching_profile_for__by_type_among_choices_"), ref, StringUtils.join("; ", type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, "Unable to find matching profile for " + ref + " (by type) among choices: " + StringUtils.join("; ", type.getTargetProfile())); } else { Map> badProfiles = new HashMap>(); Map> goodProfiles = new HashMap>(); @@ -2176,12 +2220,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (goodProfiles.size() == 0) { if (!isShowMessagesFromReferences()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles),messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); for (StructureDefinition sd : badProfiles.keySet()) { slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); } } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1,messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); for (List messages : badProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2192,12 +2236,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (!isShowMessagesFromReferences()) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); for (StructureDefinition sd : badProfiles.keySet()) { slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); } } else { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); for (List messages : goodProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2208,7 +2252,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok,messages.getString("Invalid_Resource_target_type_Found__but_expected_one_of_"), ft, types.toString()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + types.toString() + ")"); } if (type.hasAggregation()) { boolean modeOk = false; @@ -2220,14 +2264,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) modeOk = true; } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk,messages.getString("Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference"), refType); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference"); } } } if (we == null) { TypeRefComponent type = getReferenceTypeRef(container.getType()); boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); - rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef,messages.getString("Bundled_or_contained_reference_not_found_within_the_bundleresource_"), ref); + rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, "Bundled or contained reference not found within the bundle/resource " + ref); } if (we == null && ft != null && assumeValidRestReferences) { // if we == null, we inferred ft from the reference. if we are told to treat this as gospel @@ -2239,7 +2283,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat types.add(sd.getType()); } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft),messages.getString("The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_"), ft, ref, types); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), "The type '" + ft + "' implied by the reference URL " + ref + " is not a valid Target for this element (must be one of " + types + ")"); } if (pol == ReferenceValidationPolicy.CHECK_VALID) { @@ -2330,7 +2374,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List events = new ArrayList(); focus.getNamedChildren("event", events); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(),messages.getString("Expected__but_found__event_elements"), Integer.toString(fixed.getEvent().size()), Integer.toString(events.size()))) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(), + "Expected " + Integer.toString(fixed.getEvent().size()) + " but found " + Integer.toString(events.size()) + " event elements")) { for (int i = 0; i < events.size(); i++) checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); } @@ -2520,14 +2565,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fullUrl == null) { //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error // but this rule doesn't apply for batches or transactions - rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"),messages.getString("Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl")); + rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"), "Relative Reference appears inside Bundle whose entry is missing a fullUrl"); return null; } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { if (isTransaction) { - rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref),messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_"), ref); + rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref + ")"); } else { - rule(errors, IssueType.INVALID, -1, -1, path, false,messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_"), ref); + rule(errors, IssueType.INVALID, -1, -1, path, false, "Relative URLs must be of the format [ResourceName]/[id]. Encountered " + ref); } return null; @@ -2570,18 +2615,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (targetUrl.equals(we.getChildValue("fullUrl"))) { Element r = we.getNamedChild("resource"); if (version.isEmpty()) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); match = r; matchIndex = i; } else { try { if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); match = r; matchIndex = i; } } catch (Exception e) { - warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null,messages.getString("Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references"), targetUrl); + warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null, "Entries matching fullURL " + targetUrl + " should declare meta/versionId because there are version-specific references"); // If one of these things is null } } @@ -2589,9 +2634,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (match != null && resourceType != null) - rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType),messages.getString("Matching_reference_for_reference__has_resourceType_"), ref, match.getType()); + rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType), "Matching reference for reference " + ref + " has resourceType " + match.getType()); if (match == null) - warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"),messages.getString("URN_reference_is_not_locally_contained_within_the_bundle_"), ref); + warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"), "URN reference is not locally contained within the bundle " + ref); return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); } @@ -2790,7 +2835,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String type = stack.parent.parent.element.getChildValue("type"); fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary if (fullUrl == null) - rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null,messages.getString("Bundle_entry_missing_fullUrl")); + rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), + Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, "Bundle entry missing fullUrl"); } if ("Bundle".equals(stack.getElement().getType())) { String type = stack.getElement().getChildValue("type"); @@ -3261,7 +3307,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Gson gson = new Gson(); String json = gson.toJson((StringType) fixed); String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); + escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); expression.append("'" + escapedString + "'"); } else if (fixed instanceof UriType) { expression.append("'" + ((UriType) fixed).asStringValue() + "'"); @@ -3295,7 +3341,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (Element profile : profiles) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); if (!defn.getUrl().equals(profile.primitiveValue())) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null,messages.getString("Profile_reference__could_not_be_resolved_so_has_not_been_checked"), profile.primitiveValue())) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, "Profile reference '" + profile.primitiveValue() + "' could not be resolved, so has not been checked")) { startInner(hostContext, errors, resource, element, sd, stack, false); } } @@ -3368,7 +3414,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } return; } - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(),messages.getString("StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided"))) { + if (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")) { List localErrors = new ArrayList(); resTracker.startValidating(defn); trackUsage(defn, hostContext, element); @@ -3433,14 +3479,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List ewl = item.getChildren("enableWhen"); for (Element ew : ewl) { String ql = ew.getNamedChildValue("question"); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null,messages.getString("Questions_with_an_enableWhen_must_have_a_value_for_the_question_link"))) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) { Element tgt = getQuestionById(item, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null,messages.getString("Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition"))) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) { tgt = getQuestionById(questionnaire, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null,messages.getString("Unable_to_find_target__for_this_question_enableWhen"), ql)) { - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item,messages.getString("Target_for_this_question_enableWhen_cant_reference_itself"))) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find target '" + ql + "' for this question enableWhen")) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) { if (!isBefore(item, tgt, parents)) { - warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false,messages.getString("The_target_of_this_enableWhen_rule__comes_after_the_question_itself"), ql); + warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, "The target of this enableWhen rule (" + ql + ") comes after the question itself"); } } } @@ -3521,17 +3567,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String l = xhtml.getAttribute("lang"); String xl = xhtml.getAttribute("xml:lang"); if (l == null && xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); } else { if (l == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); } else if (!l.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_"), lang, l); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has a lang (" + l + "), but they differ "); } if (xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); } else if (!xl.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_"), lang, xl); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has an xml:lang (" + xl + "), but they differ "); } } } @@ -3545,7 +3591,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int i = 0; for (Element e : list) { String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); - rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s),messages.getString("Duplicate_Security_Label_"), s); + rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s), "Duplicate Security Label " + s); tags.add(s); i++; } @@ -3564,7 +3610,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (!Utilities.noString(ref)) { SearchParameter sp = context.fetchResource(SearchParameter.class, ref); if (sp != null) { - rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", sp.getType().toCode().equals(type),messages.getString("Type_mismatch__SearchParameter__type_is__but_type_here_is_"), sp.getUrl(), sp.getType().toCode(), type); + rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", + sp.getType().toCode().equals(type), "Type mismatch - SearchParameter '" + sp.getUrl() + "' type is " + sp.getType().toCode() + ", but type here is " + type); } } iSP++; @@ -3586,10 +3633,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat vs = null; } if (vs != null) { - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion"), url, vsu)) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1,messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include"), url, vsu)) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_"), url, vsu, vs.getCompose().getInclude().get(0).getSystem())) { - rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details"), url, vsu); + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but it is an expansion")) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a single include")) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a matching system (" + vs.getCompose().getInclude().get(0).getSystem() + ")")) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() + && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but the include has extra details"); } } } // todo... try getting the value set the other way... @@ -3611,11 +3659,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat questionnaire = q.getChildValue("reference"); } } - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null,messages.getString("No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire"))) { + if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { long t = System.nanoTime(); Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null,messages.getString("The_questionnaire__could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire"), questionnaire)) { + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \"" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } @@ -3678,29 +3726,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { String text = element.getNamedChildValue("text"); - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()),messages.getString("If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_"), qItem.getLinkId()); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId " + qItem.getLinkId()); List answers = new ArrayList(); element.getNamedChildren("answer", answers); if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? // GDG 2019-07-13 -// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers),messages.getString("Item_has_answer_2_even_though_it_is_not_enabled_"), qItem.getLinkId()); +// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers), "Item has answer (2), even though it is not enabled "+qItem.getLinkId()); } if (answers.size() > 1) - rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_answer_item_with_this_linkId_allowed")); + rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); for (Element answer : answers) { NodeStack ns = stack.push(answer, -1, null, null); if (qItem.getType() != null) { switch (qItem.getType()) { case GROUP: - rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false,messages.getString("Items_of_type_group_should_not_have_answers")); + rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); break; case DISPLAY: // nothing break; @@ -3774,11 +3822,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); } if (qItem.getType() == null) { - fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Definition_for_item__does_not_contain_a_type"), qItem.getLinkId()); + fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item " + qItem.getLinkId() + " does not contain a type"); } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { List items = new ArrayList(); element.getNamedChildren("item", items); - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(),messages.getString("Items_not_of_type_DISPLAY_should_not_have_items__linkId_0"), qItem.getLinkId()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); } else { validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); } @@ -3790,7 +3838,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { if (elements.size() > 1) - rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_item_with_this_linkId_allowed__"), qItem.getLinkId()); + rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); int i = 0; for (Element element : elements) { NodeStack ns = stack.push(element, i, null, null); @@ -3815,7 +3863,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int lastIndex = -1; for (Element item : items) { String linkId = item.getNamedChildValue("linkId"); - if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId),messages.getString("No_LinkId_so_cant_be_validated"))) { + if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), "No LinkId, so can't be validated")) { int index = getLinkIdIndex(qItems, linkId); if (index == -1) { QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); @@ -3824,9 +3872,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat NodeStack ns = stack.push(item, -1, null, null); validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1,messages.getString("LinkId__not_found_in_questionnaire"), linkId); + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire"); } else { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex,messages.getString("Structural_Error_items_are_out_of_order")); + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; // If an item has a child called "linkId" but no child called "answer", @@ -3853,7 +3901,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int i = 0; for (Element e : mapItem) { NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled,messages.getString("Item_has_answer_even_though_it_is_not_enabled_item_id__"), qItem.getLinkId()); + rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, "Item has answer, even though it is not enabled (item id = '" + qItem.getLinkId() + "')"); i++; } } @@ -3902,9 +3950,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return (s); } if (types.length == 1) - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_of_type_"), types[0]); + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be of type " + types[0]); else - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_one_of_the_types_"), l.toString()); + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be one of the types " + l.toString()); } return null; } @@ -3926,7 +3974,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); - if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(ref))) { + if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, "ValueSet " + describeReference(ref) + " not found by validator")) { try { Coding c = ObjectConverter.readAsCoding(value); if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { @@ -3944,7 +3992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false,messages.getString("Error__validating_Coding_against_Questionnaire_Options"), e.getMessage()); + warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options"); } } } @@ -3958,7 +4006,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (qItem.hasAnswerValueSet()) validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_options_because_no_option_or_options_are_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate options because no option or options are provided"); } private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { @@ -3986,7 +4034,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_integer")); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type integer"); } else { boolean found = false; for (IntegerType item : list) { @@ -3996,11 +4044,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_integer__is_not_a_valid_option"), v.primitiveValue()); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The integer " + v.primitiveValue() + " is not a valid option"); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_integer_answer_option_because_no_option_list_is_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate integer answer option because no option list is provided"); } private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4016,7 +4064,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_date")); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type date"); } else { boolean found = false; for (DateType item : list) { @@ -4026,11 +4074,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_date__is_not_a_valid_option"), v.primitiveValue()); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The date " + v.primitiveValue() + " is not a valid option"); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_date_answer_option_because_no_option_list_is_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate date answer option because no option list is provided"); } private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4046,7 +4094,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_time")); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type time"); } else { boolean found = false; for (TimeType item : list) { @@ -4056,11 +4104,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_time__is_not_a_valid_option"), v.primitiveValue()); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The time " + v.primitiveValue() + " is not a valid option"); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_time_answer_option_because_no_option_list_is_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate time answer option because no option list is provided"); } private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4079,7 +4127,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!openChoice) { if (list.isEmpty()) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_string")); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type string"); } else { boolean found = false; for (StringType item : list) { @@ -4089,12 +4137,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_string__is_not_a_valid_option"), v.primitiveValue()); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The string " + v.primitiveValue() + " is not a valid option"); } } } } else { - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_string_answer_option_because_no_option_list_is_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate string answer option because no option list is provided"); } } @@ -4115,7 +4163,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_coding")); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type coding"); } else { boolean found = false; for (Coding item : list) { @@ -4125,11 +4173,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_code__is_not_a_valid_option"), system, code); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The code " + system + "::" + code + " is not a valid option"); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_Coding_option_because_no_option_list_is_provided")); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate Coding option because no option list is provided"); } private String tail(String path) { @@ -4167,7 +4215,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat type = StringUtils.defaultString(type); if (entries.size() == 0) { - rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")),messages.getString("Documents_or_Messages_must_contain_at_least_one_entry")); + rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); } else { // Get the first entry, the MessageHeader Element firstEntry = entries.get(0); @@ -4179,7 +4227,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (type.equals("document")) { Element resource = firstEntry.getNamedChild("resource"); String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } checkAllInterlinked(errors, entries, stack, bundle, true); @@ -4187,7 +4235,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (type.equals("message")) { Element resource = firstEntry.getNamedChild("resource"); String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); @@ -4201,8 +4249,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String id = getIdForEntry(entry); if (url != null) { if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false,messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches"), url, fullUrl, id); - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))),messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself"), url, fullUrl); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless the resource id (" + id + ") also matches"); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless on the canonical server itself"); } // todo: check specials } @@ -4261,7 +4309,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); } - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id),messages.getString("Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__"), id, fullUrl); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id), + "Resource ID does not match the ID in the entry full URL ('" + id + "' vs '" + fullUrl + "') "); } i++; } @@ -4313,9 +4362,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (EntrySummary e : entryList) { Element entry = e.getEntry(); if (isError) { - rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); } else { - warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); } i++; } @@ -4393,7 +4442,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (ref != null && !Utilities.noString(reference)) { Element target = resolveInBundle(entries, reference, fullUrl, type, id); - rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null,messages.getString("Cant_find__in_the_bundle_"), reference, name); + rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, "Can't find '" + reference + "' in the bundle (" + name + ")"); } } @@ -4407,7 +4456,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__no_resources_allowed_here"), resourceName); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + " is not valid - no resources allowed here"); } else if (isValidResourceType(resourceName, trr)) { long t = System.nanoTime(); StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); @@ -4421,7 +4470,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat hc = hostContext.forContained(element); } trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null,messages.getString("No_profile_found_for_contained_resource_of_type_"), resourceName)) { + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'")) { validateResource(hc, errors, resource, element, profile, idstatus, stack); } } else { @@ -4433,9 +4482,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_"), resourceName, types.get(0)); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be " + types.get(0)); } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_one_of_"), resourceName, types); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be one of " + types); } } } @@ -4471,7 +4520,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { // first entry must be a composition - if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"),messages.getString("The_first_entry_in_a_document_must_be_a_composition"))) { + if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), + "The first entry in a document must be a composition")) { // the composition subject etc references must resolve in the bundle validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); @@ -4633,7 +4683,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (trc.getWorkingCode().equals("Reference")) type = "Reference"; else - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false,messages.getString("The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_"), ei.getName(), describeTypes(ei.definition.getType())); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, + "The type of element " + ei.getName() + " is not known, which is illegal. Valid types at this point are " + describeTypes(ei.definition.getType())); } } else if (ei.definition.getContentReference() != null) { typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); @@ -4691,11 +4742,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension } else if (type.equals("Extension")) { Element eurl = ei.getElement().getNamedChild("url"); - if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null,messages.getString("Extensionurl_is_required"))) { + if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, "Extension.url is required")) { String url = eurl.primitiveValue(); thisExtension = url; - if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url),messages.getString("Extensionurl_is_required"))) { - if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url),messages.getString("Extensionurl_must_be_an_absolute_URL"))) { + if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), "Extension.url is required")) { + if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), "Extension.url must be an absolute URL")) { checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); } } @@ -4717,7 +4768,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else { - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null,messages.getString("Unrecognised_Content_"), ei.getName())) + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.getName())) validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); } StructureDefinition p = null; @@ -4733,7 +4784,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); //} - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_type_"), type); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown type " + type); } } else if (profiles.size() == 1) { String url = profiles.get(0); @@ -4742,7 +4793,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat url = url.substring(0, url.indexOf("#")); } p = this.context.fetchResource(StructureDefinition.class, url); - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), profiles.get(0)); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + profiles.get(0)); } else { elementValidated = true; HashMap> goodProfiles = new HashMap>(); @@ -4755,7 +4806,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat url = url.substring(0, url.indexOf("#")); } p = this.context.fetchResource(StructureDefinition.class, typeProfile); - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), typeProfile)) { + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + typeProfile)) { List profileErrors = new ArrayList(); validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); if (hasErrors(profileErrors)) @@ -4767,7 +4818,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (goodProfiles.size() == 1) { errors.addAll(goodProfiles.values().iterator().next()); } else if (goodProfiles.size() == 0) { - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Unable_to_find_matching_profile_among_choices_"), StringUtils.join("; ", profiles)); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Unable to find matching profile among choices: " + StringUtils.join("; ", profiles)); for (String m : badProfiles.keySet()) { p = this.context.fetchResource(StructureDefinition.class, m); for (ValidationMessage message : badProfiles.get(m)) { @@ -4776,7 +4827,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else { - warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Found_multiple_matching_profiles_among_choices_"), StringUtils.join("; ", goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Found multiple matching profiles among choices: " + StringUtils.join("; ", goodProfiles.keySet())); for (String m : goodProfiles.keySet()) { p = this.context.fetchResource(StructureDefinition.class, m); for (ValidationMessage message : goodProfiles.get(m)) { @@ -4893,15 +4944,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; if (ed.getMin() > 0) { if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation"), location, Integer.toString(ed.getMin())); + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + "': Unable to check minimum required (" + Integer.toString(ed.getMin()) + ") due to lack of slicing validation"); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_minimum_required___but_only_found_"), location, Integer.toString(ed.getMin()), Integer.toString(count)); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + ": minimum required = " + Integer.toString(ed.getMin()) + ", but only found " + Integer.toString(count)); } if (ed.hasMax() && !ed.getMax().equals("*")) { if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation"), location, ed.getMax()); + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": Unable to check max allowed (" + ed.getMax() + ") due to lack of slicing validation"); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_max_allowed___but_found_"), location, ed.getMax(), Integer.toString(count)); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": max allowed = " + ed.getMax() + ", but found " + Integer.toString(count)); } } } @@ -4960,12 +5011,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)),messages.getString("This_element_does_not_match_any_known_slice_"), (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), + "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); } } else { // Don't raise this if we're in an abstract profile, like Resource if (!profile.getAbstract()) - rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null),messages.getString("This_element_is_not_allowed_by_the_profile_"), profile.getUrl()); + rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), "This element is not allowed by the profile " + profile.getUrl()); } // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements boolean isXmlAttr = false; @@ -4980,10 +5032,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok,messages.getString("As_specified_by_profile__Element__is_out_of_order"), profile.getUrl(), ei.getName()); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order"); } if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr,messages.getString("As_specified_by_profile__Element__is_out_of_order_in_ordered_slice"), profile.getUrl(), ei.getName()); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order in ordered slice"); if (ei.definition == null || !isXmlAttr) last = ei.index; if (ei.slice != null) @@ -5034,7 +5086,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (match) { boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); - if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk,messages.getString("Profile__Element_matches_more_than_one_slice___"), profile.getUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) { + if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, "Profile " + profile.getUrl() + ", Element matches more than one slice - " + (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()) + ", " + (ed.hasSliceName() ? ed.getSliceName() : ""))) { ei.definition = ed; if (ei.slice == null) { ei.index = i; @@ -5166,7 +5218,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { // first entry must be a messageheader - if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"),messages.getString("The_first_entry_in_a_message_must_be_a_MessageHeader"))) { + if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), + "The first entry in a message must be a MessageHeader")) { List elements = messageHeader.getChildren("focus"); for (Element elem : elements) validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); @@ -5198,7 +5251,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (defn == null) defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); sdTime = sdTime + (System.nanoTime() - t); - ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null,messages.getString("No_definition_found_for_resource_type_"), resourceName); + ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, "No definition found for resource type '" + resourceName + "'"); } String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); @@ -5213,13 +5266,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } // todo: validate everything in this bundle. } - ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName),messages.getString("Specified_profile_type_was__but_found_type_"), type, resourceName); + ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), "Specified profile type was '" + type + "', but found type '" + resourceName + "'"); if (ok) { if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_requires_an_id_but_none_is_present")); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource requires an id, but none is present"); else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_an_id_but_none_is_allowed")); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource has an id, but none is allowed"); start(hostContext, errors, element, element, defn, stack); // root is both definition and type } } @@ -5456,7 +5509,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); } catch (Exception e) { - System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); + System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " (\"" + inv.getExpression() + "\"): " + e.getMessage()); } } } From 9cd6460df5001c1fadc0e186ef02e9b995179ac0 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 18:26:16 +0100 Subject: [PATCH 07/52] replacing quotation marks --- .../instancevalidator/InstanceValidator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index bf4e7eced..daae2ee53 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -1404,13 +1404,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xverManager.matchingUrl(url)) { switch (xverManager.status(url)) { case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion \"" + xverManager.getVersion(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion'" + xverManager.getVersion(url) + "\")"); break; case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id \"" + xverManager.getElementId(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id'" + xverManager.getElementId(url) + "\")"); break; case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id \"" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id'" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); break; case Valid: ex = xverManager.makeDefinition(url); @@ -1906,7 +1906,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (charCount > 0 && charCount % 4 != 0) { String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value \"{0}\" is not a valid Base64 value", value); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value'{0}\" is not a valid Base64 value", value); } } } @@ -3663,7 +3663,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \"" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire'" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } @@ -3872,7 +3872,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat NodeStack ns = stack.push(item, -1, null, null); validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire"); + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId'" + linkId + "\" not found in questionnaire"); } else { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; From a80fcd345c76c636c980ee7496939f31bafcba5c Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 18:27:35 +0100 Subject: [PATCH 08/52] replacing quotation marks --- .../instancevalidator/InstanceValidator.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java index daae2ee53..2bb80b6ab 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/instancevalidator/InstanceValidator.java @@ -1095,7 +1095,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); 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 + "\")"); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { @@ -1310,7 +1310,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); 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 + "\")"); + 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, checkDisplay, stack)) if (theElementCntext != null && theElementCntext.hasBinding()) { @@ -1404,13 +1404,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xverManager.matchingUrl(url)) { switch (xverManager.status(url)) { case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion'" + xverManager.getVersion(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion'" + xverManager.getVersion(url) + "')"); break; case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id'" + xverManager.getElementId(url) + "\")"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id'" + xverManager.getElementId(url) + "')"); break; case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id'" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id'" + xverManager.getElementId(url) + "' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); break; case Valid: ex = xverManager.makeDefinition(url); @@ -1906,7 +1906,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (charCount > 0 && charCount % 4 != 0) { String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value'{0}\" is not a valid Base64 value", value); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value'{0}' is not a valid Base64 value", value); } } } @@ -3307,7 +3307,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Gson gson = new Gson(); String json = gson.toJson((StringType) fixed); String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); + escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); expression.append("'" + escapedString + "'"); } else if (fixed instanceof UriType) { expression.append("'" + ((UriType) fixed).asStringValue() + "'"); @@ -3663,7 +3663,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire'" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire'" + questionnaire + "' could not be resolved, so no validation can be performed against the base questionnaire")) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } @@ -3872,7 +3872,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat NodeStack ns = stack.push(item, -1, null, null); validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId'" + linkId + "\" not found in questionnaire"); + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId'" + linkId + "' not found in questionnaire"); } else { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; @@ -5509,7 +5509,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); } catch (Exception e) { - System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " (\"" + inv.getExpression() + "\"): " + e.getMessage()); + System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); } } } From 4cd4044a0e9304d16aeeb2a6d6f7479c0454a926 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 18:53:10 +0100 Subject: [PATCH 09/52] almost done --- .../instance/InstanceValidator.java | 654 +++++++++--------- .../src/main/resources/Messages.properties | 23 +- 2 files changed, 328 insertions(+), 349 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 29417e496..44849ccee 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -32,7 +32,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; @@ -140,7 +142,13 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; -import org.hl7.fhir.validation.instance.utils.*; +import org.hl7.fhir.validation.instance.utils.ChildIterator; +import org.hl7.fhir.validation.instance.utils.ElementInfo; +import org.hl7.fhir.validation.instance.utils.EntrySummary; +import org.hl7.fhir.validation.instance.utils.IndexedElement; +import org.hl7.fhir.validation.instance.utils.ResolvedReference; +import org.hl7.fhir.validation.instance.utils.ResourceValidationTracker; +import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.w3c.dom.Document; import com.google.gson.Gson; @@ -151,7 +159,7 @@ import ca.uhn.fhir.util.ObjectUtil; /** * Thinking of using this in a java program? Don't! - * You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine, or NativeHostServices + * You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine *

    * Validation todo: * - support @default slices @@ -368,6 +376,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private ValidatorHostServices validatorServices; private boolean assumeValidRestReferences; private boolean allowExamples; + private ResourceBundle messages = + ResourceBundle.getBundle("Messages", Locale.US); public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { super(); @@ -692,8 +702,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkElementUsage(List errors, Element element, NodeStack stack) { String elementUsage = element.getUserString("elementSupported"); - hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), - "The element " + element.getName() + " is not marked as 'mustSupport' in the profile " + element.getProperty().getStructure().getUrl() + ". Consider not using the element, or marking the element as must-Support in the profile"); + hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"),messages.getString("The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile"), element.getName(), element.getProperty().getStructure().getUrl()); if (element.hasChildren()) { String prevName = ""; @@ -724,8 +733,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List lines = new ArrayList(); focus.getNamedChildren("line", lines); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(), - "Expected " + Integer.toString(fixed.getLine().size()) + " but found " + Integer.toString(lines.size()) + " line elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(),messages.getString("Expected__but_found__line_elements"), Integer.toString(fixed.getLine().size()), Integer.toString(lines.size()))) { for (int i = 0; i < lines.size(); i++) checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); } @@ -764,7 +772,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (s.getSeverity() == IssueSeverity.WARNING) txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); else - return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage() + " for '" + system + "#" + code + "'"); + return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null,messages.getString("_for_"), s.getMessage(), system, code); return true; } else if (system.startsWith("http://hl7.org/fhir")) { if (Utilities.existsInList(system, "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/srt")) @@ -773,25 +781,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; // we don't validate these else { CodeSystem cs = getCodeSystem(system); - if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System '" + system + "'")) { + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null,messages.getString("Unknown_Code_System_"), system)) { ConceptDefinitionComponent def = getCodeDefinition(cs, code); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")")) - return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'"); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null,messages.getString("Unknown_Code_"), system, code)) + return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()),messages.getString("Display_should_be_"), def.getDisplay()); } return false; } } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { return true; // no checks in this case } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI_"), system); return false; } else { try { if (context.fetchResourceWithException(ValueSet.class, system) != null) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system + " - cannot use a value set URI as a system"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system"), system); // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. } - hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated"); + hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false,messages.getString("Code_System_URI__is_unknown_so_the_code_cannot_be_validated"), system); return true; } catch (Exception e) { return true; @@ -823,9 +831,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List codings = new ArrayList(); focus.getNamedChildren("coding", codings); 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")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() >= fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { for (int i = 0; i < fixed.getCoding().size(); i++) { Coding fixedCoding = fixed.getCoding().get(i); boolean found = false; @@ -859,9 +865,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } 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")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(),messages.getString("Expected__but_found__coding_elements"), Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { for (int i = 0; i < codings.size(); i++) checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), fixedSource, "coding", focus); } @@ -872,20 +876,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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)")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing_cc"), path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { try { CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+ ")"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_is_required_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); } } else { long t = System.nanoTime(); @@ -910,28 +914,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); 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"), cc, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } } } else { if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); 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"), cc, stack); if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } } } @@ -964,13 +968,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); } } } @@ -981,20 +985,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat 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)")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing_cc"), path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { try { CodeableConcept cc = convertToCodeableConcept(element, logical); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_is_required_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_"), describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("No_code_provided_and_a_code_should_be_provided_from_the_value_set__"), describeReference(binding.getValueSet()), valueset.getUrl()); } } else { long t = System.nanoTime(); @@ -1019,28 +1023,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bindingsOk = false; if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); 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"), cc, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__"), describeReference(binding.getValueSet()), vr.getErrorClass().toString()); } } } else { if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); 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"), cc, stack); if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__"), describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); } } } @@ -1060,7 +1064,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); if (!vr.isOk()) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Code_0_is_not_a_valid_code_in_code_system_1"), nextCode, nextSystem); } } } @@ -1069,7 +1073,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept"), e.getMessage()); } // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { @@ -1077,9 +1081,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); } } } @@ -1091,18 +1095,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String code = c.getCode(); String system = c.getSystem(); String display = c.getDisplay(); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); 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 + "')"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); try { if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) 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")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { try { long t = System.nanoTime(); ValidationResult vr = null; @@ -1112,46 +1116,46 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); 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"), c, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); } } } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); 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"), c, stack); else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); } } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); } } } } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); } } } @@ -1230,19 +1234,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided could be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("None_of_the_codes_provided_are_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_codes__"), describeReference(maxVSUrl), valueset.getUrl(), ccSummary(cc)); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); } } } @@ -1250,19 +1254,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (code = " + c.getSystem() + "#" + c.getCode() + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_is_not_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_code__"), describeReference(maxVSUrl), valueset.getUrl(), c.getSystem(), c.getCode()); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); } } } @@ -1270,19 +1274,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { // TODO Auto-generated method stub ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(maxVSUrl))) { try { long t = System.nanoTime(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); txTime = txTime + (System.nanoTime() - t); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__"), describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_code_provided_is_not_in_the_maximum_value_set__"), describeReference(maxVSUrl), valueset.getUrl(), "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_CodeableConcept_using_maxValueSet"), e.getMessage()); } } } @@ -1306,18 +1310,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String code = element.getNamedChildValue("code"); String system = element.getNamedChildValue("system"); String display = element.getNamedChildValue("display"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Codingsystem_must_be_an_absolute_reference_not_a_local_reference")); 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 + "')"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system),messages.getString("The_Coding_references_a_value_set_not_a_code_system_"), system); try { if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) 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")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null,messages.getString("Binding_for__missing"), path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(binding.getValueSet()))) { try { Coding c = ObjectConverter.readAsCoding(element); long t = System.nanoTime(); @@ -1328,46 +1332,46 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server")); else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required"), describeReference(binding.getValueSet(), valueset)); 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"), c, stack); else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), describeReference(binding.getValueSet(), valueset)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set"), describeReference(binding.getValueSet(), valueset)); } } } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set. " + getErrorMessage(vr.getMessage())); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); 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"), c, stack); else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code. " + getErrorMessage(vr.getMessage())); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set. " + getErrorMessage(vr.getMessage())); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_"), describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); } } } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding"), e.getMessage()); } } } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_by_URI_reference_cannot_be_checked")); } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Binding_for_path__has_no_source_so_cant_be_checked"), path); } } } } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("Error__validating_Coding_"), e.getMessage(), e.toString()); } } } @@ -1403,13 +1407,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xverManager.matchingUrl(url)) { switch (xverManager.status(url)) { case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalidVersion'" + xverManager.getVersion(url) + "')"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_invalidVersion"), url, xverManager.getVersion(url)); break; case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (unknown Element id'" + xverManager.getElementId(url) + "')"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_unknown_Element_id"), url, xverManager.getElementId(url)); break; case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id'" + xverManager.getElementId(url) + "' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__is_not_valid_Element_id_is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions"), url, xverManager.getElementId(url)); break; case Valid: ex = xverManager.makeDefinition(url); @@ -1417,44 +1421,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat context.cacheResource(ex); break; default: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' evaluation state illegal"); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false,messages.getString("Extension_url__evaluation_state_illegal"), url); break; } } else if (extensionUrl != null && !isAbsolute(url)) { if (extensionUrl.equals(profile.getUrl())) { - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), "Sub-extension url '" + url + "' is not defined by the Extension " + profile.getUrl()); + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url),messages.getString("Subextension_url__is_not_defined_by_the_Extension_"), url, profile.getUrl()); } - } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), "The extension " + url + " is unknown, and not allowed here")) { - hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), "Unknown extension " + url); + } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url),messages.getString("The_extension__is_unknown_and_not_allowed_here"), url)) { + hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url),messages.getString("Unknown_extension_"), url); } } if (ex != null) { trackUsage(ex, hostContext, element); if (def.getIsModifier()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not")); } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is")); } // two questions // 1. can this extension be used here? checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); if (isModifier) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must be used as a modifierExtension"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_be_used_as_a_modifierExtension"), url); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must not be used as an extension (it's a modifierExtension)"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(),messages.getString("The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension"), url); // check the type of the extension: Set allowedTypes = listExtensionTypes(ex); String actualType = getExtensionType(element); if (actualType == null) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(),messages.getString("The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions"), url); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType), "The Extension '" + url + "' definition allows for the types " + allowedTypes.toString() + " but found type " + actualType); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType),messages.getString("The_Extension__definition_allows_for_the_types__but_found_type_"), url, allowedTypes.toString(), 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, true, url); @@ -1579,14 +1579,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!ok) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, "The extension " + extUrl + " is not allowed to be used at this point (allowed = " + contexts.toString() + "; this element is [" + plist.toString() + ")"); + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_"), extUrl, contexts.toString(), plist.toString()); return false; } else { if (definition.hasContextInvariant()) { for (StringType s : definition.getContextInvariant()) { if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, - "The extension " + extUrl + " is not allowed to be used at this point (based on context invariant '" + s.getValue() + "')"); + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false,messages.getString("The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_"), extUrl, s.getValue()); return false; } } @@ -1635,53 +1634,39 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if ((fixed == null || fixed.isEmpty()) && focus == null) { ; // this is all good } else if ((fixed == null || fixed.isEmpty()) && focus != null) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern, "The element " + focus.getName() + " is present in the instance but not allowed in the applicable " + (pattern ? "pattern" : "fixed value") + " specified in profile"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern,messages.getString("The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile"), focus.getName(), (pattern ? "pattern" : "fixed value")); } else if (fixed != null && !fixed.isEmpty() && focus == null) { - rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false, "Missing element '" + propName + "' - required by fixed value assigned in profile " + fixedSource); + rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false,messages.getString("Missing_element___required_by_fixed_value_assigned_in_profile_"), propName, fixedSource); } else { String value = focus.primitiveValue(); if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.CodeType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.CodeType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue()); else if (fixed instanceof org.hl7.fhir.r5.model.StringType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.StringType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.StringType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.UriType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UriType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UriType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.DateType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.OidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.OidType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.OidType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UuidType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.UuidType) fixed).getValue()); else if (fixed instanceof org.hl7.fhir.r5.model.IdType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IdType) fixed).getValue() + "'"); + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value),messages.getString("Value_is__but_must_be_"), value, ((org.hl7.fhir.r5.model.IdType) fixed).getValue()); else if (fixed instanceof Quantity) checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); else if (fixed instanceof Address) @@ -1710,16 +1695,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); else - rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false, "Unhandled fixed value type " + fixed.getClass().getName()); + rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false,messages.getString("Unhandled_fixed_value_type_"), fixed.getClass().getName()); List extensions = new ArrayList(); focus.getNamedChildren("extension", extensions); if (fixed.getExtension().size() == 0) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0, "No extensions allowed, as the specified fixed value doesn't contain any extensions"); - } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(), - "Extensions count mismatch: expected " + Integer.toString(fixed.getExtension().size()) + " but found " + Integer.toString(extensions.size()))) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0,messages.getString("No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions")); + } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(),messages.getString("Extensions_count_mismatch_expected__but_found_"), Integer.toString(fixed.getExtension().size()), Integer.toString(extensions.size()))) { for (Extension e : fixed.getExtension()) { Element ex = getExtensionByUrl(extensions, e.getUrl()); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, "Extension count mismatch: unable to find extension: " + e.getUrl())) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null,messages.getString("Extension_count_mismatch_unable_to_find_extension_"), e.getUrl())) { checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); } } @@ -1734,26 +1718,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List parts = new ArrayList(); focus.getNamedChildren("family", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), - "Expected " + (fixed.hasFamily() ? "1" : "0") + " but found " + Integer.toString(parts.size()) + " family elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(),messages.getString("Expected__but_found__family_elements"), (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); } focus.getNamedChildren("given", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), - "Expected " + Integer.toString(fixed.getGiven().size()) + " but found " + Integer.toString(parts.size()) + " given elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(),messages.getString("Expected__but_found__given_elements"), Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); } focus.getNamedChildren("prefix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), - "Expected " + Integer.toString(fixed.getPrefix().size()) + " but found " + Integer.toString(parts.size()) + " prefix elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(),messages.getString("Expected__but_found__prefix_elements"), Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); } focus.getNamedChildren("suffix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), - "Expected " + Integer.toString(fixed.getSuffix().size()) + " but found " + Integer.toString(parts.size()) + " suffix elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(),messages.getString("Expected__but_found__suffix_elements"), Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) { for (int i = 0; i < parts.size(); i++) checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); } @@ -1761,7 +1741,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { String system = element.getNamedChildValue("system"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Identifier.system must be an absolute reference, not a local reference"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system),messages.getString("Identifiersystem_must_be_an_absolute_reference_not_a_local_reference")); } private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { @@ -1781,40 +1761,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { if (isBlank(e.primitiveValue())) { if (e.primitiveValue() == null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_or_must_have_child_extensions")); else if (e.primitiveValue().length() == 0) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value that is not empty"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_must_have_a_value_that_is_not_empty")); else if (StringUtils.isWhitespace(e.primitiveValue())) - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types should not only be whitespace"); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(),messages.getString("Primitive_types_should_not_only_be_whitespace")); return; } String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); if (regex != null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), "Element value '" + e.primitiveValue() + "' does not meet regex '" + regex + "'"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex),messages.getString("Element_value__does_not_meet_regex_"), e.primitiveValue(), regex); if (type.equals("boolean")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()),messages.getString("boolean_values_must_be_true_or_false")); } if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { String url = e.primitiveValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), "URI values cannot start with oid:"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), "URI values cannot start with uuid:"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"),messages.getString("URI_values_cannot_start_with_oid")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"),messages.getString("URI_values_cannot_start_with_uuid")); rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) // work around an old invalid example in a core package - || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), "URI values cannot have whitespace('" + url + "')"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); + || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url),messages.getString("URI_values_cannot_have_whitespace"), url); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); if (type.equals("oid")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), "OIDs must start with urn:oid:")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), "OIDs must be valid"); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"),messages.getString("OIDs_must_start_with_urnoid"))) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)),messages.getString("OIDs_must_be_valid")); } if (type.equals("uuid")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"),messages.getString("UUIDs_must_start_with_urnuuid")); try { UUID.fromString(url.substring(8)); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid (" + ex.getMessage() + ")"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("UUIDs_must_be_valid_"), ex.getMessage()); } } @@ -1826,57 +1806,54 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } catch (IOException e1) { found = false; } - rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '" + url + "' does not resolve"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, found,messages.getString("URL_value__does_not_resolve"), url); } } if (type.equals("id")) { // work around an old issue with ElementDefinition.id if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), "id value '" + e.primitiveValue() + "' is not valid"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()),messages.getString("id_value__is_not_valid"), e.primitiveValue()); } } if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, "@value cannot be empty")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), "value should not start or finish with whitespace"); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, "value is longer than permitted maximum length of 1 MB (1048576 bytes)")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0,messages.getString("value_cannot_be_empty"))) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()),messages.getString("value_should_not_start_or_finish_with_whitespace")); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576,messages.getString("value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes"))) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); } } } if (type.equals("dateTime")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() - .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), - "Not a valid date time"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), "if a date has a time, it must have a timezone"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); + .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"),messages.getString("Not_a_valid_date_time")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()),messages.getString("if_a_date_has_a_time_it_must_have_a_timezone")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); try { DateTimeType dt = new DateTimeType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date/time (" + ex.getMessage() + ")"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_datetime_"), ex.getMessage()); } } if (type.equals("time")) { rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() - .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"), - "Not a valid time"); + .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"),messages.getString("Not_a_valid_time")); try { TimeType dt = new TimeType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid time (" + ex.getMessage() + ")"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_time_"), ex.getMessage()); } } if (type.equals("date")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), - "Not a valid date"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum value of " + context.getMaxLength()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"),messages.getString("Not_a_valid_date")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_value_of_"), context.getMaxLength()); try { DateType dt = new DateType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date (" + ex.getMessage() + ")"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_date_"), ex.getMessage()); } } if (type.equals("base64Binary")) { @@ -1905,56 +1882,55 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (charCount > 0 && charCount % 4 != 0) { String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value'{0}' is not a valid Base64 value", value); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("The_value0_is_not_a_valid_Base64_value"), value); } } } if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer"), e.primitiveValue())) { Integer v = new Integer(e.getValue()).intValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); } } if (type.equals("integer64")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer64")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()),messages.getString("The_value__is_not_a_valid_integer64"), e.primitiveValue())) { Long v = new Long(e.getValue()).longValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v),messages.getString("value_is_greater_than_permitted_maximum_value_of_"), (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v),messages.getString("value_is_less_than_permitted_minimum_value_of_"), (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0,messages.getString("value_is_less_than_permitted_minimum_value_of_0")); if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0,messages.getString("value_is_less_than_permitted_minimum_value_of_1")); } } if (type.equals("decimal")) { if (e.primitiveValue() != null) { DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is not a valid decimal")) - warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is outside the range of commonly/reasonably supported decimals"); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE,messages.getString("The_value__is_not_a_valid_decimal"), e.primitiveValue())) + warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE,messages.getString("The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals"), e.primitiveValue()); } } if (type.equals("instant")) { rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), - "The instant '" + e.primitiveValue() + "' is not valid (by regex)"); - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); + e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"),messages.getString("The_instant__is_not_valid_by_regex"), e.primitiveValue()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()),messages.getString("The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error"), e.primitiveValue()); try { InstantType dt = new InstantType(e.primitiveValue()); } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid instant (" + ex.getMessage() + ")"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Not_a_valid_instant_"), ex.getMessage()); } } if (type.equals("code") && e.primitiveValue() != null) { // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace // other than single spaces in the contents - rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()), "The code '" + e.primitiveValue() + "' is not valid (whitespace rules)"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()),messages.getString("The_code__is_not_valid_whitespace_rules"), e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(),messages.getString("value_is_longer_than_permitted_maximum_length_of_"), context.getMaxLength()); } if (context.hasBinding() && e.primitiveValue() != null) { @@ -1966,10 +1942,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (xhtml != null) { // if it is null, this is an error already noted in the parsers // check that the namespace is there and correct. String ns = xhtml.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); // check that inner namespaces are all correct checkInnerNS(errors, e, path, xhtml.getChildNodes()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), "Wrong name on the XHTML ('" + ns + "') - must start with div"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()),messages.getString("Wrong_name_on_the_XHTML___must_start_with_div"), ns); // check that no illegal elements and attributes have been used checkInnerNames(errors, e, path, xhtml.getChildNodes()); } @@ -1999,7 +1975,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", "code", "samp", "img", "map", "area" - ), "Illegal element name in the XHTML ('" + node.getName() + "')"); + ),messages.getString("Illegal_element_name_in_the_XHTML_"), node.getName()); for (String an : node.getAttributes().keySet()) { boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", @@ -2013,7 +1989,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" ); if (!ok) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Illegal attribute name in the XHTML ('" + an + "' on '" + node.getName() + "')"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false,messages.getString("Illegal_attribute_name_in_the_XHTML__on_"), an, node.getName()); } checkInnerNames(errors, e, path, node.getChildNodes()); } @@ -2024,7 +2000,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (XhtmlNode node : list) { if (node.getNodeType() == NodeType.Element) { String ns = node.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns),messages.getString("Wrong_namespace_on_the_XHTML__should_be_"), ns, FormatUtilities.XHTML_NS); checkInnerNS(errors, e, path, node.getChildNodes()); } } @@ -2045,7 +2021,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ElementDefinitionBindingComponent binding = elementContext.getBinding(); if (binding.hasValueSet()) { ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, "ValueSet {0} not found by validator", describeReference(binding.getValueSet()))) { + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null,messages.getString("ValueSet_0_not_found_by_validator"), describeReference(binding.getValueSet()))) { long t = System.nanoTime(); ValidationResult vr = null; if (binding.getStrength() != BindingStrength.EXAMPLE) { @@ -2054,23 +2030,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') could not be validated in the absence of a terminology server"); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server"), value); else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)" + getErrorMessage(vr.getMessage())); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_is_required_from_this_value_set"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); 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) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)" + getErrorMessage(vr.getMessage())); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is recommended to come from this value set)" + getErrorMessage(vr.getMessage())); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false,messages.getString("The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set"), value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); } } } } } else if (!noBindingMsgSuppressed) - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), "Binding has no source, so can't be checked"); + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"),messages.getString("Binding_has_no_source_so_cant_be_checked")); } private void checkQuantity(List errors, String path, Element focus, Quantity fixed, String fixedSource, boolean pattern) { @@ -2100,7 +2076,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String ref = reference.getReference(); if (Utilities.noString(ref)) { if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display"); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")),messages.getString("A_Reference_without_an_actual_reference_or_identifier_should_have_a_display")); } return; } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { @@ -2143,7 +2119,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat we = ext == null ? null : makeExternalRef(ext, path); } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), "Unable to resolve resource '" + ref + "'"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS),messages.getString("Unable_to_resolve_resource_"), ref); } String ft; @@ -2165,16 +2141,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat break; } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, - "The type '" + reference.getType() + "' is not a valid Target for this element (must be one of " + container.getType("Reference").getTargetProfile() + ")"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource,messages.getString("The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_"), reference.getType(), container.getType("Reference").getTargetProfile()); } // the type has to match the actual - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), "The specified type '" + reference.getType() + "' does not match the found type '" + ft + "'"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()),messages.getString("The_specified_type__does_not_match_the_found_type_"), reference.getType(), ft); } if (we != null && pol.checkType()) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, "Unable to determine type of target resource")) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null,messages.getString("Unable_to_determine_type_of_target_resource"))) { // we validate as much as we can. First, can we infer a type from the profile? boolean ok = false; TypeRefComponent type = getReferenceTypeRef(container.getType()); @@ -2183,7 +2158,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List profiles = new ArrayList<>(); for (UriType u : type.getTargetProfile()) { StructureDefinition sd = resolveProfile(profile, u.getValue()); - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, "Unable to resolve the profile reference '" + u.getValue() + "'")) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null,messages.getString("Unable_to_resolve_the_profile_reference_"), u.getValue())) { types.add(sd.getType()); if (ft.equals(sd.getType())) { ok = true; @@ -2192,7 +2167,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!pol.checkValid()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, "Unable to find matching profile for " + ref + " (by type) among choices: " + StringUtils.join("; ", type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0,messages.getString("Unable_to_find_matching_profile_for__by_type_among_choices_"), ref, StringUtils.join("; ", type.getTargetProfile())); } else { Map> badProfiles = new HashMap>(); Map> goodProfiles = new HashMap>(); @@ -2219,12 +2194,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (goodProfiles.size() == 0) { if (!isShowMessagesFromReferences()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles),messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); for (StructureDefinition sd : badProfiles.keySet()) { slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); } } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1,messages.getString("Unable_to_find_matching_profile_for__among_choices_"), ref, asList(type.getTargetProfile())); for (List messages : badProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2235,12 +2210,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (!isShowMessagesFromReferences()) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); for (StructureDefinition sd : badProfiles.keySet()) { slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); } } else { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,messages.getString("Found_multiple_matching_profiles_for__among_choices_"), ref, asListByUrl(goodProfiles.keySet())); for (List messages : goodProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2251,7 +2226,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + types.toString() + ")"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok,messages.getString("Invalid_Resource_target_type_Found__but_expected_one_of_"), ft, types.toString()); } if (type.hasAggregation()) { boolean modeOk = false; @@ -2263,14 +2238,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) modeOk = true; } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk,messages.getString("Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference"), refType); } } } if (we == null) { TypeRefComponent type = getReferenceTypeRef(container.getType()); boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); - rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, "Bundled or contained reference not found within the bundle/resource " + ref); + rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef,messages.getString("Bundled_or_contained_reference_not_found_within_the_bundleresource_"), ref); } if (we == null && ft != null && assumeValidRestReferences) { // if we == null, we inferred ft from the reference. if we are told to treat this as gospel @@ -2282,7 +2257,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat types.add(sd.getType()); } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), "The type '" + ft + "' implied by the reference URL " + ref + " is not a valid Target for this element (must be one of " + types + ")"); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft),messages.getString("The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_"), ft, ref, types); } if (pol == ReferenceValidationPolicy.CHECK_VALID) { @@ -2373,8 +2348,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List events = new ArrayList(); focus.getNamedChildren("event", events); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(), - "Expected " + Integer.toString(fixed.getEvent().size()) + " but found " + Integer.toString(events.size()) + " event elements")) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(),messages.getString("Expected__but_found__event_elements"), Integer.toString(fixed.getEvent().size()), Integer.toString(events.size()))) { for (int i = 0; i < events.size(); i++) checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); } @@ -2564,14 +2538,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fullUrl == null) { //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error // but this rule doesn't apply for batches or transactions - rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"), "Relative Reference appears inside Bundle whose entry is missing a fullUrl"); + rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"),messages.getString("Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl")); return null; } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { if (isTransaction) { - rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref + ")"); + rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref),messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_"), ref); } else { - rule(errors, IssueType.INVALID, -1, -1, path, false, "Relative URLs must be of the format [ResourceName]/[id]. Encountered " + ref); + rule(errors, IssueType.INVALID, -1, -1, path, false,messages.getString("Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_"), ref); } return null; @@ -2614,18 +2588,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (targetUrl.equals(we.getChildValue("fullUrl"))) { Element r = we.getNamedChild("resource"); if (version.isEmpty()) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); match = r; matchIndex = i; } else { try { if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null,messages.getString("Multiple_matches_in_bundle_for_reference_"), ref); match = r; matchIndex = i; } } catch (Exception e) { - warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null, "Entries matching fullURL " + targetUrl + " should declare meta/versionId because there are version-specific references"); + warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null,messages.getString("Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references"), targetUrl); // If one of these things is null } } @@ -2633,9 +2607,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (match != null && resourceType != null) - rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType), "Matching reference for reference " + ref + " has resourceType " + match.getType()); + rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType),messages.getString("Matching_reference_for_reference__has_resourceType_"), ref, match.getType()); if (match == null) - warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"), "URN reference is not locally contained within the bundle " + ref); + warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"),messages.getString("URN_reference_is_not_locally_contained_within_the_bundle_"), ref); return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); } @@ -2835,7 +2809,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary if (fullUrl == null) rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), - Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, "Bundle entry missing fullUrl"); + Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null,messages.getString("Bundle_entry_missing_fullUrl")); } if ("Bundle".equals(stack.getElement().getType())) { String type = stack.getElement().getChildValue("type"); @@ -3340,7 +3314,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (Element profile : profiles) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); if (!defn.getUrl().equals(profile.primitiveValue())) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, "Profile reference '" + profile.primitiveValue() + "' could not be resolved, so has not been checked")) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null,messages.getString("Profile_reference__could_not_be_resolved_so_has_not_been_checked"), profile.primitiveValue())) { startInner(hostContext, errors, resource, element, sd, stack, false); } } @@ -3413,7 +3387,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } return; } - if (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")) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(),messages.getString("StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided"))) { List localErrors = new ArrayList(); resTracker.startValidating(defn); trackUsage(defn, hostContext, element); @@ -3478,14 +3452,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List ewl = item.getChildren("enableWhen"); for (Element ew : ewl) { String ql = ew.getNamedChildValue("question"); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null,messages.getString("Questions_with_an_enableWhen_must_have_a_value_for_the_question_link"))) { Element tgt = getQuestionById(item, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null,messages.getString("Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition"))) { tgt = getQuestionById(questionnaire, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find target '" + ql + "' for this question enableWhen")) { - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null,messages.getString("Unable_to_find_target__for_this_question_enableWhen"), ql)) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item,messages.getString("Target_for_this_question_enableWhen_cant_reference_itself"))) { if (!isBefore(item, tgt, parents)) { - warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, "The target of this enableWhen rule (" + ql + ") comes after the question itself"); + warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false,messages.getString("The_target_of_this_enableWhen_rule__comes_after_the_question_itself"), ql); } } } @@ -3566,17 +3540,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String l = xhtml.getAttribute("lang"); String xl = xhtml.getAttribute("xml:lang"); if (l == null && xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); } else { if (l == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); } else if (!l.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has a lang (" + l + "), but they differ "); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_"), lang, l); } if (xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues")); } else if (!xl.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has an xml:lang (" + xl + "), but they differ "); + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_"), lang, xl); } } } @@ -3590,7 +3564,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int i = 0; for (Element e : list) { String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); - rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s), "Duplicate Security Label " + s); + rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s),messages.getString("Duplicate_Security_Label_"), s); tags.add(s); i++; } @@ -3610,7 +3584,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat SearchParameter sp = context.fetchResource(SearchParameter.class, ref); if (sp != null) { rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", - sp.getType().toCode().equals(type), "Type mismatch - SearchParameter '" + sp.getUrl() + "' type is " + sp.getType().toCode() + ", but type here is " + type); + sp.getType().toCode().equals(type),messages.getString("Type_mismatch__SearchParameter__type_is__but_type_here_is_"), sp.getUrl(), sp.getType().toCode(), type); } } iSP++; @@ -3632,16 +3606,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat vs = null; } if (vs != null) { - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but it is an expansion")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a single include")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a matching system (" + vs.getCompose().getInclude().get(0).getSystem() + ")")) { + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion"), url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1,messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include"), url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_"), url, vsu, vs.getCompose().getInclude().get(0).getSystem())) { rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() - && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but the include has extra details"); + && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(),messages.getString("CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details"), url, vsu); } } } // todo... try getting the value set the other way... } + private void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { Element q = element.getNamedChild("questionnaire"); String questionnaire = null; @@ -3658,11 +3633,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat questionnaire = q.getChildValue("reference"); } } - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { + if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null,messages.getString("No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire"))) { long t = System.nanoTime(); Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire'" + questionnaire + "' could not be resolved, so no validation can be performed against the base questionnaire")) { + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null,messages.getString("The_questionnaire_could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire"), questionnaire)) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } @@ -3725,29 +3700,29 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { String text = element.getNamedChildValue("text"); - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId " + qItem.getLinkId()); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()),messages.getString("If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_"), qItem.getLinkId()); List answers = new ArrayList(); element.getNamedChildren("answer", answers); if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers),messages.getString("No_response_answer_found_for_required_item_"), qItem.getLinkId()); } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? // GDG 2019-07-13 -// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers), "Item has answer (2), even though it is not enabled "+qItem.getLinkId()); +// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers),messages.getString("Item_has_answer_2_even_though_it_is_not_enabled_"), qItem.getLinkId()); } if (answers.size() > 1) - rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); + rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_answer_item_with_this_linkId_allowed")); for (Element answer : answers) { NodeStack ns = stack.push(answer, -1, null, null); if (qItem.getType() != null) { switch (qItem.getType()) { case GROUP: - rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); + rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false,messages.getString("Items_of_type_group_should_not_have_answers")); break; case DISPLAY: // nothing break; @@ -3821,11 +3796,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); } if (qItem.getType() == null) { - fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item " + qItem.getLinkId() + " does not contain a type"); + fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Definition_for_item__does_not_contain_a_type"), qItem.getLinkId()); } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { List items = new ArrayList(); element.getNamedChildren("item", items); - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(),messages.getString("Items_not_of_type_DISPLAY_should_not_have_items__linkId_0"), qItem.getLinkId()); } else { validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); } @@ -3837,7 +3812,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { if (elements.size() > 1) - rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); + rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(),messages.getString("Only_one_response_item_with_this_linkId_allowed__"), qItem.getLinkId()); int i = 0; for (Element element : elements) { NodeStack ns = stack.push(element, i, null, null); @@ -3862,7 +3837,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int lastIndex = -1; for (Element item : items) { String linkId = item.getNamedChildValue("linkId"); - if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), "No LinkId, so can't be validated")) { + if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId),messages.getString("No_LinkId_so_cant_be_validated"))) { int index = getLinkIdIndex(qItems, linkId); if (index == -1) { QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); @@ -3871,9 +3846,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat NodeStack ns = stack.push(item, -1, null, null); validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId'" + linkId + "' not found in questionnaire"); + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1,messages.getString("LinkId_not_found_in_questionnaire"), linkId); } else { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex,messages.getString("Structural_Error_items_are_out_of_order")); lastIndex = index; // If an item has a child called "linkId" but no child called "answer", @@ -3900,7 +3875,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int i = 0; for (Element e : mapItem) { NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, "Item has answer, even though it is not enabled (item id = '" + qItem.getLinkId() + "')"); + rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled,messages.getString("Item_has_answer_even_though_it_is_not_enabled_item_id__"), qItem.getLinkId()); i++; } } @@ -3949,9 +3924,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return (s); } if (types.length == 1) - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be of type " + types[0]); + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_of_type_"), types[0]); else - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be one of the types " + l.toString()); + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false,messages.getString("Answer_value_must_be_one_of_the_types_"), l.toString()); } return null; } @@ -3973,7 +3948,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); - if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, "ValueSet " + describeReference(ref) + " not found by validator")) { + if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null,messages.getString("ValueSet__not_found_by_validator"), describeReference(ref))) { try { Coding c = ObjectConverter.readAsCoding(value); if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { @@ -3986,12 +3961,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ValidationResult res = context.validateCode(new ValidationOptions(stack.workingLang), c, vs); txTime = txTime + (System.nanoTime() - t); if (!res.isOk()) { - txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire"); + txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false,messages.getString("The_value_provided__is_not_in_the_options_value_set_in_the_questionnaire"), c.getSystem(), c.getCode()); } else if (res.getSeverity() != null) { super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); } } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options"); + warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false,messages.getString("Error__validating_Coding_against_Questionnaire_Options"), e.getMessage()); } } } @@ -4005,7 +3980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (qItem.hasAnswerValueSet()) validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate options because no option or options are provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_options_because_no_option_or_options_are_provided")); } private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { @@ -4033,7 +4008,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type integer"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_integer")); } else { boolean found = false; for (IntegerType item : list) { @@ -4043,11 +4018,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The integer " + v.primitiveValue() + " is not a valid option"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_integer__is_not_a_valid_option"), v.primitiveValue()); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate integer answer option because no option list is provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_integer_answer_option_because_no_option_list_is_provided")); } private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4063,7 +4038,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type date"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_date")); } else { boolean found = false; for (DateType item : list) { @@ -4073,11 +4048,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The date " + v.primitiveValue() + " is not a valid option"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_date__is_not_a_valid_option"), v.primitiveValue()); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate date answer option because no option list is provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_date_answer_option_because_no_option_list_is_provided")); } private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4093,7 +4068,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type time"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_time")); } else { boolean found = false; for (TimeType item : list) { @@ -4103,11 +4078,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The time " + v.primitiveValue() + " is not a valid option"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_time__is_not_a_valid_option"), v.primitiveValue()); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate time answer option because no option list is provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_time_answer_option_because_no_option_list_is_provided")); } private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { @@ -4126,7 +4101,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!openChoice) { if (list.isEmpty()) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type string"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_string")); } else { boolean found = false; for (StringType item : list) { @@ -4136,12 +4111,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The string " + v.primitiveValue() + " is not a valid option"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_string__is_not_a_valid_option"), v.primitiveValue()); } } } } else { - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate string answer option because no option list is provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_string_answer_option_because_no_option_list_is_provided")); } } @@ -4162,7 +4137,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type coding"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Option_list_has_no_option_values_of_type_coding")); } else { boolean found = false; for (Coding item : list) { @@ -4172,11 +4147,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The code " + system + "::" + code + " is not a valid option"); + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found,messages.getString("The_code__is_not_a_valid_option"), system, code); } } } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate Coding option because no option list is provided"); + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false,messages.getString("Cannot_validate_Coding_option_because_no_option_list_is_provided")); } private String tail(String path) { @@ -4214,7 +4189,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat type = StringUtils.defaultString(type); if (entries.size() == 0) { - rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); + rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")),messages.getString("Documents_or_Messages_must_contain_at_least_one_entry")); } else { // Get the first entry, the MessageHeader Element firstEntry = entries.get(0); @@ -4226,7 +4201,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (type.equals("document")) { Element resource = firstEntry.getNamedChild("resource"); String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } checkAllInterlinked(errors, entries, stack, bundle, true); @@ -4234,7 +4209,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (type.equals("message")) { Element resource = firstEntry.getNamedChild("resource"); String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null,messages.getString("No_resource_on_first_entry"))) { validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); @@ -4248,8 +4223,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String id = getIdForEntry(entry); if (url != null) { if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless the resource id (" + id + ") also matches"); - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless on the canonical server itself"); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false,messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches"), url, fullUrl, id); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))),messages.getString("The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself"), url, fullUrl); } // todo: check specials } @@ -4308,8 +4283,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); } - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id), - "Resource ID does not match the ID in the entry full URL ('" + id + "' vs '" + fullUrl + "') "); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id),messages.getString("Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__"), id, fullUrl); } i++; } @@ -4361,9 +4335,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (EntrySummary e : entryList) { Element entry = e.getEntry(); if (isError) { - rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); + rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); } else { - warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); + warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e),messages.getString("Entry__isnt_reachable_by_traversing_from_first_Bundle_entry"), (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); } i++; } @@ -4441,7 +4415,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (ref != null && !Utilities.noString(reference)) { Element target = resolveInBundle(entries, reference, fullUrl, type, id); - rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, "Can't find '" + reference + "' in the bundle (" + name + ")"); + rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null,messages.getString("Cant_find__in_the_bundle_"), reference, name); } } @@ -4455,7 +4429,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + " is not valid - no resources allowed here"); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__no_resources_allowed_here"), resourceName); } else if (isValidResourceType(resourceName, trr)) { long t = System.nanoTime(); StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); @@ -4469,7 +4443,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat hc = hostContext.forContained(element); } trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'")) { + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null,messages.getString("No_profile_found_for_contained_resource_of_type_"), resourceName)) { validateResource(hc, errors, resource, element, profile, idstatus, stack); } } else { @@ -4481,9 +4455,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be " + types.get(0)); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_"), resourceName, types.get(0)); } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be one of " + types); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("The_type__is_not_valid__must_be_one_of_"), resourceName, types); } } } @@ -4519,8 +4493,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { // first entry must be a composition - if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), - "The first entry in a document must be a composition")) { + if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"),messages.getString("The_first_entry_in_a_document_must_be_a_composition"))) { // the composition subject etc references must resolve in the bundle validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); @@ -4682,8 +4655,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (trc.getWorkingCode().equals("Reference")) type = "Reference"; else - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, - "The type of element " + ei.getName() + " is not known, which is illegal. Valid types at this point are " + describeTypes(ei.definition.getType())); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false,messages.getString("The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_"), ei.getName(), describeTypes(ei.definition.getType())); } } else if (ei.definition.getContentReference() != null) { typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); @@ -4741,11 +4713,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension } else if (type.equals("Extension")) { Element eurl = ei.getElement().getNamedChild("url"); - if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, "Extension.url is required")) { + if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null,messages.getString("Extensionurl_is_required"))) { String url = eurl.primitiveValue(); thisExtension = url; - if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), "Extension.url is required")) { - if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), "Extension.url must be an absolute URL")) { + if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url),messages.getString("Extensionurl_is_required"))) { + if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url),messages.getString("Extensionurl_must_be_an_absolute_URL"))) { checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); } } @@ -4767,7 +4739,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else { - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.getName())) + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null,messages.getString("Unrecognised_Content_"), ei.getName())) validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); } StructureDefinition p = null; @@ -4783,7 +4755,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); //} - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown type " + type); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_type_"), type); } } else if (profiles.size() == 1) { String url = profiles.get(0); @@ -4792,7 +4764,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat url = url.substring(0, url.indexOf("#")); } p = this.context.fetchResource(StructureDefinition.class, url); - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + profiles.get(0)); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), profiles.get(0)); } else { elementValidated = true; HashMap> goodProfiles = new HashMap>(); @@ -4805,7 +4777,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat url = url.substring(0, url.indexOf("#")); } p = this.context.fetchResource(StructureDefinition.class, typeProfile); - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + typeProfile)) { + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null,messages.getString("Unknown_profile_"), typeProfile)) { List profileErrors = new ArrayList(); validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); if (hasErrors(profileErrors)) @@ -4817,7 +4789,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (goodProfiles.size() == 1) { errors.addAll(goodProfiles.values().iterator().next()); } else if (goodProfiles.size() == 0) { - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Unable to find matching profile among choices: " + StringUtils.join("; ", profiles)); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Unable_to_find_matching_profile_among_choices_"), StringUtils.join("; ", profiles)); for (String m : badProfiles.keySet()) { p = this.context.fetchResource(StructureDefinition.class, m); for (ValidationMessage message : badProfiles.get(m)) { @@ -4826,7 +4798,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else { - warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Found multiple matching profiles among choices: " + StringUtils.join("; ", goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false,messages.getString("Found_multiple_matching_profiles_among_choices_"), StringUtils.join("; ", goodProfiles.keySet())); for (String m : goodProfiles.keySet()) { p = this.context.fetchResource(StructureDefinition.class, m); for (ValidationMessage message : goodProfiles.get(m)) { @@ -4943,15 +4915,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; if (ed.getMin() > 0) { if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + "': Unable to check minimum required (" + Integer.toString(ed.getMin()) + ") due to lack of slicing validation"); + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation"), location, Integer.toString(ed.getMin())); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + ": minimum required = " + Integer.toString(ed.getMin()) + ", but only found " + Integer.toString(count)); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(),messages.getString("_minimum_required___but_only_found_"), location, Integer.toString(ed.getMin()), Integer.toString(count)); } if (ed.hasMax() && !ed.getMax().equals("*")) { if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": Unable to check max allowed (" + ed.getMax() + ") due to lack of slicing validation"); + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation"), location, ed.getMax()); else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": max allowed = " + ed.getMax() + ", but found " + Integer.toString(count)); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()),messages.getString("_max_allowed___but_found_"), location, ed.getMax(), Integer.toString(count)); } } } @@ -5010,13 +4982,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), - "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)),messages.getString("This_element_does_not_match_any_known_slice_"), (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); } } else { // Don't raise this if we're in an abstract profile, like Resource if (!profile.getAbstract()) - rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), "This element is not allowed by the profile " + profile.getUrl()); + rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null),messages.getString("This_element_is_not_allowed_by_the_profile_"), profile.getUrl()); } // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements boolean isXmlAttr = false; @@ -5031,10 +5002,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order"); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok,messages.getString("As_specified_by_profile__Element__is_out_of_order"), profile.getUrl(), ei.getName()); } if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order in ordered slice"); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr,messages.getString("As_specified_by_profile__Element__is_out_of_order_in_ordered_slice"), profile.getUrl(), ei.getName()); if (ei.definition == null || !isXmlAttr) last = ei.index; if (ei.slice != null) @@ -5085,7 +5056,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (match) { boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); - if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, "Profile " + profile.getUrl() + ", Element matches more than one slice - " + (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()) + ", " + (ed.hasSliceName() ? ed.getSliceName() : ""))) { + if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk,messages.getString("Profile__Element_matches_more_than_one_slice___"), profile.getUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) { ei.definition = ed; if (ei.slice == null) { ei.index = i; @@ -5217,8 +5188,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { // first entry must be a messageheader - if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), - "The first entry in a message must be a MessageHeader")) { + if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"),messages.getString("The_first_entry_in_a_message_must_be_a_MessageHeader"))) { List elements = messageHeader.getChildren("focus"); for (Element elem : elements) validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); @@ -5250,7 +5220,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (defn == null) defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); sdTime = sdTime + (System.nanoTime() - t); - ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, "No definition found for resource type '" + resourceName + "'"); + ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null,messages.getString("No_definition_found_for_resource_type_"), resourceName); } String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); @@ -5265,13 +5235,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } // todo: validate everything in this bundle. } - ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), "Specified profile type was '" + type + "', but found type '" + resourceName + "'"); + ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName),messages.getString("Specified_profile_type_was__but_found_type_"), type, resourceName); if (ok) { if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource requires an id, but none is present"); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_requires_an_id_but_none_is_present")); else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource has an id, but none is allowed"); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Resource_has_an_id_but_none_is_allowed")); start(hostContext, errors, element, element, defn, stack); // root is both definition and type } } diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 398338220..19ba4a1e4 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -1,5 +1,6 @@ The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile Expected__but_found__line_elements = Expected {0} but found {1} line elements +_for_ = {0} for '{1}#{2}' Unknown_Code_System_ = Unknown Code System '{0}' Unknown_Code_ = Unknown Code ({0}#{1}) Display_should_be_ = Display should be '{0}' @@ -9,17 +10,18 @@ Code_System_URI__is_unknown_so_the_code_cannot_be_validated = Code System URI '{ Expected__but_found__coding_elements = Expected {0} but found {1} coding elements Binding_for__missing_cc = Binding for {0} missing (cc) ValueSet__not_found_by_validator = ValueSet {0} not found by validator +No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_ = No code provided, and a code must be provided from the value set {0} (max value set {1}) No_code_provided_and_a_code_should_be_provided_from_the_value_set__ = No code provided, and a code should be provided from the value set {0} ({1}) Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__ = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__ = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__ = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__ = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__ = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) Error__validating_CodeableConcept = Error {0} validating CodeableConcept Binding_by_URI_reference_cannot_be_checked = Binding by URI reference cannot be checked Binding_for_path__has_no_source_so_cant_be_checked = Binding for path {0} has no source, so can't be checked -No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) Code_0_is_not_a_valid_code_in_code_system_1 = Code {0} is not a valid code in code system {1} Codingsystem_must_be_an_absolute_reference_not_a_local_reference = Coding.system must be an absolute reference, not a local reference The_Coding_references_a_value_set_not_a_code_system_ = The Coding references a value set, not a code system ('{0}') @@ -28,18 +30,23 @@ The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set = The Coding provided is not in the value set {0}, and a code is required from this value set{1} The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} Error__validating_Coding = Error {0} validating Coding Error__validating_Coding_ = Error {0} validating Coding: {1} None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__ = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +None_of_the_codes_provided_are_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) Error__validating_CodeableConcept_using_maxValueSet = Error {0} validating CodeableConcept using maxValueSet The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__ = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +The_code_provided_is_not_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_code__ = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +The_code_provided_is_not_in_the_maximum_value_set__ = The code provided is not in the maximum value set {0} ({1}{2} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_ = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Extension_url__is_not_valid_invalidVersion_ = Extension url '{0}' is not valid (invalidVersion '{1}') -Extension_url__is_not_valid_unknown_Element_id_ = Extension url '{0}' is not valid (unknown Element id '{1}') -Extension_url__is_not_valid_Element_id__is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_url__is_not_valid_invalidVersion = Extension url '{0}' is not valid (invalidVersion'{1}') +Extension_url__is_not_valid_unknown_Element_id = Extension url '{0}' is not valid (unknown Element id'{1}') +Extension_url__is_not_valid_Element_id_is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id'{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) Extension_url__evaluation_state_illegal = Extension url '{0}' evaluation state illegal Subextension_url__is_not_defined_by_the_Extension_ = Sub-extension url '{0}' is not defined by the Extension {1} The_extension__is_unknown_and_not_allowed_here = The extension {0} is unknown, and not allowed here @@ -91,7 +98,7 @@ Not_a_valid_time_ = Not a valid time ({0}) Not_a_valid_date = Not a valid date value_is_longer_than_permitted_maximum_value_of_ = value is longer than permitted maximum value of {0} Not_a_valid_date_ = Not a valid date ({0}) -The_value_0_is_not_a_valid_Base64_value = The value '{0}' is not a valid Base64 value +The_value0_is_not_a_valid_Base64_value = The value'{0}' is not a valid Base64 value The_value__is_not_a_valid_integer = The value '{0}' is not a valid integer value_is_greater_than_permitted_maximum_value_of_ = value is greater than permitted maximum value of {0} value_is_less_than_permitted_minimum_value_of_ = value is less than permitted minimum value of {0} @@ -109,6 +116,7 @@ Illegal_element_name_in_the_XHTML_ = Illegal element name in the XHTML ('{0}') Illegal_attribute_name_in_the_XHTML__on_ = Illegal attribute name in the XHTML ('{0}' on '{1}') ValueSet_0_not_found_by_validator = ValueSet {0} not found by validator The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided ('{0}') could not be validated in the absence of a terminology server +The_value_provided__is_not_in_the_value_set___and_a_code_is_required_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} Binding_has_no_source_so_cant_be_checked = Binding has no source, so can't be checked @@ -153,7 +161,7 @@ CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include = Co CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_ = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire = No questionnaire is identified, so no validation can be performed against the base questionnaire -The_questionnaire__could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +The_questionnaire_could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire'{0}' could not be resolved, so no validation can be performed against the base questionnaire If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_ = If text exists, it must match the questionnaire definition for linkId {0} No_response_answer_found_for_required_item_ = No response answer found for required item {0} Item_has_answer_2_even_though_it_is_not_enabled_ = Item has answer (2), even though it is not enabled {0} @@ -163,11 +171,12 @@ Definition_for_item__does_not_contain_a_type = Definition for item {0} does not Items_not_of_type_DISPLAY_should_not_have_items__linkId_0 = Items not of type DISPLAY should not have items - linkId {0} Only_one_response_item_with_this_linkId_allowed__ = Only one response item with this linkId allowed - {0} No_LinkId_so_cant_be_validated = No LinkId, so can't be validated -LinkId__not_found_in_questionnaire = LinkId '{0}' not found in questionnaire +LinkId_not_found_in_questionnaire = LinkId'{0}' not found in questionnaire Structural_Error_items_are_out_of_order = Structural Error: items are out of order Item_has_answer_even_though_it_is_not_enabled_item_id__ = Item has answer, even though it is not enabled (item id = '{0}') Answer_value_must_be_of_type_ = Answer value must be of type {0} Answer_value_must_be_one_of_the_types_ = Answer value must be one of the types {0} +The_value_provided__is_not_in_the_options_value_set_in_the_questionnaire = The value provided ({0}::{1}) is not in the options value set in the questionnaire Error__validating_Coding_against_Questionnaire_Options = Error {0} validating Coding against Questionnaire Options Cannot_validate_options_because_no_option_or_options_are_provided = Cannot validate options because no option or options are provided Option_list_has_no_option_values_of_type_integer = Option list has no option values of type integer From 2a2d9575e5bb4b0caee951405b5fd769fee95d07 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 19:00:52 +0100 Subject: [PATCH 10/52] added messageArgument aware method --- .../java/org/hl7/fhir/validation/BaseValidator.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 6327a9e4f..68eb86af0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -149,7 +149,7 @@ public class BaseValidator { } return thePass; } - + /** * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails. And mark it as a slicing hint for later recovery if appropriate * @@ -279,6 +279,8 @@ public class BaseValidator { * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ + + //todo: delete this when finished i18n protected boolean rule(List errors, IssueType type, String path, boolean thePass, String msg) { if (!thePass) { addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR); @@ -286,6 +288,14 @@ public class BaseValidator { return thePass; } + protected boolean rule(List errors, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) { + if (!thePass) { + String message = formatMessage(theMessage, theMessageArguments); + addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.ERROR); + } + return thePass; + } + public boolean rule(List errors, Source source, IssueType type, String path, boolean thePass, String msg) { if (!thePass) { addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.ERROR, source); From 40d6cd4108b8a7f4857b2e47aba50553a06166fc Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Tue, 25 Feb 2020 19:16:59 +0100 Subject: [PATCH 11/52] added messageArgument aware method --- .../main/java/org/hl7/fhir/validation/BaseValidator.java | 9 +++++++++ .../hl7/fhir/validation/instance/InstanceValidator.java | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 68eb86af0..3c81d0ec3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -70,6 +70,7 @@ public class BaseValidator { * Set this parameter to false if the validation does not pass * @return Returns thePass (in other words, returns true if the rule did not fail validation) */ + //todo: remove after i18n implementation done protected boolean fail(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { if (!thePass) { addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.FATAL); @@ -77,6 +78,14 @@ public class BaseValidator { return thePass; } + protected boolean fail(List errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { + if (!thePass) { + String msg = formatMessage(theMessage, theMessageArguments); + addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.FATAL); + } + return thePass; + } + /** * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails * diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 44849ccee..a516c401c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -3798,7 +3798,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (qItem.getType() == null) { fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false,messages.getString("Definition_for_item__does_not_contain_a_type"), qItem.getLinkId()); } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { - List items = new ArrayList(); + List items = new ArrayList<>(); element.getNamedChildren("item", items); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(),messages.getString("Items_not_of_type_DISPLAY_should_not_have_items__linkId_0"), qItem.getLinkId()); } else { From 5984a0c6930e3c2d52b1644c537b53e7b1c6a9b1 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Tue, 25 Feb 2020 14:14:40 -0500 Subject: [PATCH 12/52] Fixing bad quotation in string.substring --- .../org/hl7/fhir/validation/instance/InstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index a516c401c..e4dbb67d1 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -3280,7 +3280,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat Gson gson = new Gson(); String json = gson.toJson((StringType) fixed); String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",'myStringValue") - 1); + escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); expression.append("'" + escapedString + "'"); } else if (fixed instanceof UriType) { expression.append("'" + ((UriType) fixed).asStringValue() + "'"); From f8d4b2b49d1e5203910de9a6969b7fc0e135e228 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Wed, 26 Feb 2020 12:59:24 +0100 Subject: [PATCH 13/52] moved Locale to IWorkerContext moved LocaleHandling into BaseValidator --- .../fhir/r5/context/BaseWorkerContext.java | 17 ++ .../hl7/fhir/r5/context/IWorkerContext.java | 7 +- .../hl7/fhir/validation/BaseValidator.java | 20 +- .../validation/profile/ProfileValidator.java | 5 +- .../main/resources/Messages_de_DE.properties | 230 ++++++++++++++++++ 5 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index b7d119827..6f6437b41 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -30,7 +30,9 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.StringUtils; @@ -94,6 +96,8 @@ import com.google.gson.JsonObject; public abstract class BaseWorkerContext implements IWorkerContext { + private Locale locale; + public class MetadataResourceVersionComparator implements Comparator { private List list; @@ -417,6 +421,19 @@ public abstract class BaseWorkerContext implements IWorkerContext { this.expandCodesLimit = expandCodesLimit; } + @Override + public Locale getLocale() { + if (Objects.nonNull(locale)){ + return locale; + } else { + return Locale.US; + } + } + + @Override + public void setLocale(Locale locale) { + this.locale = locale; + } @Override public ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heirarchical) throws FHIRException { ValueSet vs = null; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 11c4a94cf..242055e44 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -24,6 +24,7 @@ import java.util.EnumSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -327,7 +328,11 @@ public interface IWorkerContext { * @throws FHIRException */ public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean hierarchical) throws TerminologyServiceException; - + + Locale getLocale(); + + void setLocale(Locale locale); + public class ValidationResult { private ConceptDefinitionComponent definition; private IssueSeverity severity; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 3c81d0ec3..cac38fb49 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -53,7 +53,10 @@ POSSIBILITY OF SUCH DAMAGE. import java.text.MessageFormat; import java.util.List; +import java.util.ResourceBundle; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; @@ -62,7 +65,19 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source; public class BaseValidator { protected Source source; - + protected IWorkerContext context; + private ResourceBundle messages; + + public BaseValidator(IWorkerContext context){ + this.context = context; + messages = ResourceBundle.getBundle("Messages", context.getLocale() ); + } + + public void setContext(IWorkerContext context) { + this.context = context; + messages = ResourceBundle.getBundle("Messages", context.getLocale() ); + } + /** * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails * @@ -134,7 +149,7 @@ public class BaseValidator { private String formatMessage(String theMessage, Object... theMessageArguments) { String message; if (theMessageArguments != null && theMessageArguments.length > 0) { - message = MessageFormat.format(theMessage, theMessageArguments); + message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); } else { message = theMessage; } @@ -546,5 +561,4 @@ public class BaseValidator { } return thePass; } - } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java index 054adb258..01db7feb0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java @@ -37,12 +37,11 @@ import org.hl7.fhir.validation.BaseValidator; public class ProfileValidator extends BaseValidator { - IWorkerContext context; private boolean checkAggregation = false; private boolean checkMustSupport = false; - public void setContext(IWorkerContext context) { - this.context = context; + public ProfileValidator(IWorkerContext context) { + super(context); } public boolean isCheckAggregation() { diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties new file mode 100644 index 000000000..19ba4a1e4 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -0,0 +1,230 @@ +The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Expected__but_found__line_elements = Expected {0} but found {1} line elements +_for_ = {0} for '{1}#{2}' +Unknown_Code_System_ = Unknown Code System '{0}' +Unknown_Code_ = Unknown Code ({0}#{1}) +Display_should_be_ = Display should be '{0}' +Invalid_System_URI_ = Invalid System URI: {0} +Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system = Invalid System URI: {0} - cannot use a value set URI as a system +Code_System_URI__is_unknown_so_the_code_cannot_be_validated = Code System URI '{0}' is unknown so the code cannot be validated +Expected__but_found__coding_elements = Expected {0} but found {1} coding elements +Binding_for__missing_cc = Binding for {0} missing (cc) +ValueSet__not_found_by_validator = ValueSet {0} not found by validator +No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) +No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_ = No code provided, and a code must be provided from the value set {0} (max value set {1}) +No_code_provided_and_a_code_should_be_provided_from_the_value_set__ = No code provided, and a code should be provided from the value set {0} ({1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__ = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__ = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__ = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__ = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__ = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Error__validating_CodeableConcept = Error {0} validating CodeableConcept +Binding_by_URI_reference_cannot_be_checked = Binding by URI reference cannot be checked +Binding_for_path__has_no_source_so_cant_be_checked = Binding for path {0} has no source, so can't be checked +Code_0_is_not_a_valid_code_in_code_system_1 = Code {0} is not a valid code in code system {1} +Codingsystem_must_be_an_absolute_reference_not_a_local_reference = Coding.system must be an absolute reference, not a local reference +The_Coding_references_a_value_set_not_a_code_system_ = The Coding references a value set, not a code system ('{0}') +Binding_for__missing = Binding for {0} missing +The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided could not be validated in the absence of a terminology server +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set = The Coding provided is not in the value set {0}, and a code is required from this value set{1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Error__validating_Coding = Error {0} validating Coding +Error__validating_Coding_ = Error {0} validating Coding: {1} +None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__ = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +None_of_the_codes_provided_are_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Error__validating_CodeableConcept_using_maxValueSet = Error {0} validating CodeableConcept using maxValueSet +The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__ = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +The_code_provided_is_not_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_code__ = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +The_code_provided_is_not_in_the_maximum_value_set__ = The code provided is not in the maximum value set {0} ({1}{2} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_ = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Extension_url__is_not_valid_invalidVersion = Extension url '{0}' is not valid (invalidVersion'{1}') +Extension_url__is_not_valid_unknown_Element_id = Extension url '{0}' is not valid (unknown Element id'{1}') +Extension_url__is_not_valid_Element_id_is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id'{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_url__evaluation_state_illegal = Extension url '{0}' evaluation state illegal +Subextension_url__is_not_defined_by_the_Extension_ = Sub-extension url '{0}' is not defined by the Extension {1} +The_extension__is_unknown_and_not_allowed_here = The extension {0} is unknown, and not allowed here +Unknown_extension_ = Unknown extension {0} +Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is +The_Extension__must_be_used_as_a_modifierExtension = The Extension '{0}' must be used as a modifierExtension +The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +The_Extension__definition_allows_for_the_types__but_found_type_ = The Extension '{0}' definition allows for the types {1} but found type {2} +The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_ = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) +The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_ = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Missing_element___required_by_fixed_value_assigned_in_profile_ = Missing element '{0}' - required by fixed value assigned in profile {1} +Value_is__but_must_be_ = Value is '{0}' but must be '{1}' +Unhandled_fixed_value_type_ = Unhandled fixed value type {0} +No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions = No extensions allowed, as the specified fixed value doesn't contain any extensions +Extensions_count_mismatch_expected__but_found_ = Extensions count mismatch: expected {0} but found {1} +Extension_count_mismatch_unable_to_find_extension_ = Extension count mismatch: unable to find extension: {0} +Expected__but_found__family_elements = Expected {0} but found {1} family elements +Expected__but_found__given_elements = Expected {0} but found {1} given elements +Expected__but_found__prefix_elements = Expected {0} but found {1} prefix elements +Expected__but_found__suffix_elements = Expected {0} but found {1} suffix elements +Identifiersystem_must_be_an_absolute_reference_not_a_local_reference = Identifier.system must be an absolute reference, not a local reference +Primitive_types_must_have_a_value_or_must_have_child_extensions = Primitive types must have a value or must have child extensions +Primitive_types_must_have_a_value_that_is_not_empty = Primitive types must have a value that is not empty +Primitive_types_should_not_only_be_whitespace = Primitive types should not only be whitespace +Element_value__does_not_meet_regex_ = Element value '{0}' does not meet regex '{1}' +boolean_values_must_be_true_or_false = boolean values must be 'true' or 'false' +URI_values_cannot_start_with_oid = URI values cannot start with oid: +URI_values_cannot_start_with_uuid = URI values cannot start with uuid: +URI_values_cannot_have_whitespace = URI values cannot have whitespace('{0}') +value_is_longer_than_permitted_maximum_length_of_ = value is longer than permitted maximum length of {0} +OIDs_must_start_with_urnoid = OIDs must start with urn:oid: +OIDs_must_be_valid = OIDs must be valid +UUIDs_must_start_with_urnuuid = UUIDs must start with urn:uuid: +UUIDs_must_be_valid_ = UUIDs must be valid ({0}) +URL_value__does_not_resolve = URL value '{0}' does not resolve +id_value__is_not_valid = id value '{0}' is not valid +value_cannot_be_empty = @value cannot be empty +value_should_not_start_or_finish_with_whitespace = value should not start or finish with whitespace +value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes = value is longer than permitted maximum length of 1 MB (1048576 bytes) +The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error = The value '{0}' is outside the range of reasonable years - check for data entry error +Not_a_valid_date_time = Not a valid date time +if_a_date_has_a_time_it_must_have_a_timezone = if a date has a time, it must have a timezone +Not_a_valid_datetime_ = Not a valid date/time ({0}) +Not_a_valid_time = Not a valid time +Not_a_valid_time_ = Not a valid time ({0}) +Not_a_valid_date = Not a valid date +value_is_longer_than_permitted_maximum_value_of_ = value is longer than permitted maximum value of {0} +Not_a_valid_date_ = Not a valid date ({0}) +The_value0_is_not_a_valid_Base64_value = The value'{0}' is not a valid Base64 value +The_value__is_not_a_valid_integer = The value '{0}' is not a valid integer +value_is_greater_than_permitted_maximum_value_of_ = value is greater than permitted maximum value of {0} +value_is_less_than_permitted_minimum_value_of_ = value is less than permitted minimum value of {0} +value_is_less_than_permitted_minimum_value_of_0 = value is less than permitted minimum value of 0 +value_is_less_than_permitted_minimum_value_of_1 = value is less than permitted minimum value of 1 +The_value__is_not_a_valid_integer64 = The value '{0}' is not a valid integer64 +The_value__is_not_a_valid_decimal = The value '{0}' is not a valid decimal +The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals = The value '{0}' is outside the range of commonly/reasonably supported decimals +The_instant__is_not_valid_by_regex = The instant '{0}' is not valid (by regex) +Not_a_valid_instant_ = Not a valid instant ({0}) +The_code__is_not_valid_whitespace_rules = The code '{0}' is not valid (whitespace rules) +Wrong_namespace_on_the_XHTML__should_be_ = Wrong namespace on the XHTML ('{0}', should be '{1}') +Wrong_name_on_the_XHTML___must_start_with_div = Wrong name on the XHTML ('{0}') - must start with div +Illegal_element_name_in_the_XHTML_ = Illegal element name in the XHTML ('{0}') +Illegal_attribute_name_in_the_XHTML__on_ = Illegal attribute name in the XHTML ('{0}' on '{1}') +ValueSet_0_not_found_by_validator = ValueSet {0} not found by validator +The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided ('{0}') could not be validated in the absence of a terminology server +The_value_provided__is_not_in_the_value_set___and_a_code_is_required_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} +The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Binding_has_no_source_so_cant_be_checked = Binding has no source, so can't be checked +A_Reference_without_an_actual_reference_or_identifier_should_have_a_display = A Reference without an actual reference or identifier should have a display +Unable_to_resolve_resource_ = Unable to resolve resource '{0}' +The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' is not a valid Target for this element (must be one of {1}) +The_specified_type__does_not_match_the_found_type_ = The specified type '{0}' does not match the found type '{1}' +Unable_to_determine_type_of_target_resource = Unable to determine type of target resource +Unable_to_resolve_the_profile_reference_ = Unable to resolve the profile reference '{0}' +Unable_to_find_matching_profile_for__by_type_among_choices_ = Unable to find matching profile for {0} (by type) among choices: {1} +Unable_to_find_matching_profile_for__among_choices_ = Unable to find matching profile for {0} among choices: {1} +Found_multiple_matching_profiles_for__among_choices_ = Found multiple matching profiles for {0} among choices: {1} +Invalid_Resource_target_type_Found__but_expected_one_of_ = Invalid Resource target type. Found {0}, but expected one of ({1}) +Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Bundled_or_contained_reference_not_found_within_the_bundleresource_ = Bundled or contained reference not found within the bundle/resource {0} +The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' implied by the reference URL {1} is not a valid Target for this element (must be one of {2}) +Expected__but_found__event_elements = Expected {0} but found {1} event elements +Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_ = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) +Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_ = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Multiple_matches_in_bundle_for_reference_ = Multiple matches in bundle for reference {0} +Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references +Matching_reference_for_reference__has_resourceType_ = Matching reference for reference {0} has resourceType {1} +URN_reference_is_not_locally_contained_within_the_bundle_ = URN reference is not locally contained within the bundle {0} +Bundle_entry_missing_fullUrl = Bundle entry missing fullUrl +Profile_reference__could_not_be_resolved_so_has_not_been_checked = Profile reference '{0}' could not be resolved, so has not been checked +StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided +Questions_with_an_enableWhen_must_have_a_value_for_the_question_link = Questions with an enableWhen must have a value for the question link +Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Unable_to_find_target__for_this_question_enableWhen = Unable to find target '{0}' for this question enableWhen +Target_for_this_question_enableWhen_cant_reference_itself = Target for this question enableWhen can't reference itself +The_target_of_this_enableWhen_rule__comes_after_the_question_itself = The target of this enableWhen rule ({0}) comes after the question itself +Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_ = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_ = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Duplicate_Security_Label_ = Duplicate Security Label {0} +Type_mismatch__SearchParameter__type_is__but_type_here_is_ = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_ = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire = No questionnaire is identified, so no validation can be performed against the base questionnaire +The_questionnaire_could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire'{0}' could not be resolved, so no validation can be performed against the base questionnaire +If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_ = If text exists, it must match the questionnaire definition for linkId {0} +No_response_answer_found_for_required_item_ = No response answer found for required item {0} +Item_has_answer_2_even_though_it_is_not_enabled_ = Item has answer (2), even though it is not enabled {0} +Only_one_response_answer_item_with_this_linkId_allowed = Only one response answer item with this linkId allowed +Items_of_type_group_should_not_have_answers = Items of type group should not have answers +Definition_for_item__does_not_contain_a_type = Definition for item {0} does not contain a type +Items_not_of_type_DISPLAY_should_not_have_items__linkId_0 = Items not of type DISPLAY should not have items - linkId {0} +Only_one_response_item_with_this_linkId_allowed__ = Only one response item with this linkId allowed - {0} +No_LinkId_so_cant_be_validated = No LinkId, so can't be validated +LinkId_not_found_in_questionnaire = LinkId'{0}' not found in questionnaire +Structural_Error_items_are_out_of_order = Structural Error: items are out of order +Item_has_answer_even_though_it_is_not_enabled_item_id__ = Item has answer, even though it is not enabled (item id = '{0}') +Answer_value_must_be_of_type_ = Answer value must be of type {0} +Answer_value_must_be_one_of_the_types_ = Answer value must be one of the types {0} +The_value_provided__is_not_in_the_options_value_set_in_the_questionnaire = The value provided ({0}::{1}) is not in the options value set in the questionnaire +Error__validating_Coding_against_Questionnaire_Options = Error {0} validating Coding against Questionnaire Options +Cannot_validate_options_because_no_option_or_options_are_provided = Cannot validate options because no option or options are provided +Option_list_has_no_option_values_of_type_integer = Option list has no option values of type integer +The_integer__is_not_a_valid_option = The integer {0} is not a valid option +Cannot_validate_integer_answer_option_because_no_option_list_is_provided = Cannot validate integer answer option because no option list is provided +Option_list_has_no_option_values_of_type_date = Option list has no option values of type date +The_date__is_not_a_valid_option = The date {0} is not a valid option +Cannot_validate_date_answer_option_because_no_option_list_is_provided = Cannot validate date answer option because no option list is provided +Option_list_has_no_option_values_of_type_time = Option list has no option values of type time +The_time__is_not_a_valid_option = The time {0} is not a valid option +Cannot_validate_time_answer_option_because_no_option_list_is_provided = Cannot validate time answer option because no option list is provided +Option_list_has_no_option_values_of_type_string = Option list has no option values of type string +The_string__is_not_a_valid_option = The string {0} is not a valid option +Cannot_validate_string_answer_option_because_no_option_list_is_provided = Cannot validate string answer option because no option list is provided +Option_list_has_no_option_values_of_type_coding = Option list has no option values of type coding +The_code__is_not_a_valid_option = The code {0}::{1} is not a valid option +Cannot_validate_Coding_option_because_no_option_list_is_provided = Cannot validate Coding option because no option list is provided +Documents_or_Messages_must_contain_at_least_one_entry = Documents or Messages must contain at least one entry +No_resource_on_first_entry = No resource on first entry +The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself +Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__ = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Entry__isnt_reachable_by_traversing_from_first_Bundle_entry = Entry {0} isn't reachable by traversing from first Bundle entry +Cant_find__in_the_bundle_ = Can't find '{0}' in the bundle ({1}) +The_type__is_not_valid__no_resources_allowed_here = The type '{0} is not valid - no resources allowed here +No_profile_found_for_contained_resource_of_type_ = No profile found for contained resource of type '{0}' +The_type__is_not_valid__must_be_ = The type '{0}' is not valid - must be {1} +The_type__is_not_valid__must_be_one_of_ = The type '{0}' is not valid - must be one of {1} +The_first_entry_in_a_document_must_be_a_composition = The first entry in a document must be a composition +The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_ = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Extensionurl_is_required = Extension.url is required +Extensionurl_must_be_an_absolute_URL = Extension.url must be an absolute URL +Unrecognised_Content_ = Unrecognised Content {0} +Unknown_type_ = Unknown type {0} +Unknown_profile_ = Unknown profile {0} +Unable_to_find_matching_profile_among_choices_ = Unable to find matching profile among choices: {0} +Found_multiple_matching_profiles_among_choices_ = Found multiple matching profiles among choices: {0} +_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +_minimum_required___but_only_found_ = {0}: minimum required = {1}, but only found {2} +_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +_max_allowed___but_found_ = {0}: max allowed = {1}, but found {2} +This_element_does_not_match_any_known_slice_ = This element does not match any known slice {0} +This_element_is_not_allowed_by_the_profile_ = This element is not allowed by the profile {0} +As_specified_by_profile__Element__is_out_of_order = As specified by profile {0}, Element '{1}' is out of order +As_specified_by_profile__Element__is_out_of_order_in_ordered_slice = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Profile__Element_matches_more_than_one_slice___ = Profile {0}, Element matches more than one slice - {1}, {2} +The_first_entry_in_a_message_must_be_a_MessageHeader = The first entry in a message must be a MessageHeader +No_definition_found_for_resource_type_ = No definition found for resource type '{0}' +Specified_profile_type_was__but_found_type_ = Specified profile type was '{0}', but found type '{1}' +Resource_requires_an_id_but_none_is_present = Resource requires an id, but none is present +Resource_has_an_id_but_none_is_allowed = Resource has an id, but none is allowed From 8b329d283be8325e72a81ffaab1951c6bca4f9ee Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 18:04:08 +0100 Subject: [PATCH 14/52] almost ready to merge replaced all property keys, added String Constant Class --- .../codesystem/CodeSystemValidator.java | 5 + .../instance/InstanceValidator.java | 10382 ++++++++-------- .../fhir/validation/utils/I18nConstants.java | 229 + .../src/main/resources/Messages.properties | 456 +- .../main/resources/Messages_de_DE.properties | 456 +- 5 files changed, 5857 insertions(+), 5671 deletions(-) create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemValidator.java index 492a113a7..d86d9d786 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemValidator.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -34,6 +35,10 @@ import org.hl7.fhir.validation.BaseValidator; public class CodeSystemValidator extends BaseValidator { + public CodeSystemValidator(IWorkerContext context) { + super(context); + } + public List validate(CodeSystem cs, boolean forBuild) { List errors = new ArrayList(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index ef8207ce2..0fc1020f4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -141,6 +141,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.validation.instance.utils.*; +import org.hl7.fhir.validation.utils.I18nConstants; import org.w3c.dom.Document; import com.google.gson.Gson; @@ -166,3139 +167,3074 @@ import ca.uhn.fhir.util.ObjectUtil; public class InstanceValidator extends BaseValidator implements IResourceValidator { - private class ValidatorHostServices implements IEvaluationContext { - - @Override - public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (externalHostServices != null) - return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); - else - return null; - } - - @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (externalHostServices != null) - return externalHostServices.resolveConstantType(c.getAppContext(), name); - else - return null; - } - - @Override - public boolean log(String argument, List focus) { - if (externalHostServices != null) - return externalHostServices.log(argument, focus); - else - return false; - } - - @Override - public FunctionDetails resolveFunction(String functionName) { - throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); - } - - @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); - } - - @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { - throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); - } - - @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { - ValidatorHostContext c = (ValidatorHostContext) appContext; - - if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { - return (Base) refContext.getUserData("validator.bundle.resolution"); - } - - if (c.getAppContext() instanceof Element) { - Element bnd = (Element) c.getAppContext(); - Base res = resolveInBundle(url, bnd); - if (res != null) - return res; - } - Base res = resolveInBundle(url, c.getResource()); - if (res != null) - return res; - res = resolveInBundle(url, c.getContainer()); - if (res != null) - return res; - - if (externalHostServices != null) - return externalHostServices.resolveReference(c.getAppContext(), url, refContext); - else if (fetcher != null) - try { - return fetcher.fetch(c.getAppContext(), url); - } catch (IOException e) { - throw new FHIRException(e); - } - else - throw new Error("Not done yet - resolve " + url + " locally (2)"); - - } - - public Base resolveInBundle(String url, Element bnd) { - if (bnd == null) - return null; - if (bnd.fhirType().equals("Bundle")) { - for (Element be : bnd.getChildrenByName("entry")) { - Element res = be.getNamedChild("resource"); - if (res != null) { - String fullUrl = be.getChildValue("fullUrl"); - String rt = res.fhirType(); - String id = res.getChildValue("id"); - if (url.equals(fullUrl)) - return res; - if (url.equals(rt + "/" + id)) - return res; - } - } - } - return null; - } - - @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { - ValidatorHostContext ctxt = (ValidatorHostContext) appContext; - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - if (sd == null) { - throw new FHIRException("Unable to resolve " + url); - } - InstanceValidator self = InstanceValidator.this; - List valerrors = new ArrayList(); - if (item instanceof Resource) { - try { - Element e = new ObjectConverter(context).convert((Resource) item); - self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); - } catch (IOException e1) { - throw new FHIRException(e1); - } - } else if (item instanceof Element) { - Element e = (Element) item; - if (e.isResource()) { - self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); - } else { - throw new FHIRException("Not supported yet"); - } - } else - throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); - boolean ok = true; - List record = new ArrayList<>(); - for (ValidationMessage v : valerrors) { - ok = ok && !v.getLevel().isError(); - if (v.getLevel().isError() || v.isSlicingHint()) { - record.add(v); - } - } - if (!ok && !record.isEmpty()) { - ctxt.sliceNotes(url, record); - } - return ok; - } - - @Override - public ValueSet resolveValueSet(Object appContext, String url) { - ValidatorHostContext c = (ValidatorHostContext) appContext; - if (c.getProfile() != null && url.startsWith("#")) { - for (Resource r : c.getProfile().getContained()) { - if (r.getId().equals(url.substring(1))) { - if (r instanceof ValueSet) - return (ValueSet) r; - else - throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); - } - } - return null; - } - return context.fetchResource(ValueSet.class, url); - } - - } - - private IWorkerContext context; - private FHIRPathEngine fpe; - - // configuration items - private CheckDisplayOption checkDisplay; - private boolean anyExtensionsAllowed; - private boolean errorForUnknownProfiles; - private boolean noInvariantChecks; - private boolean noTerminologyChecks; - private boolean hintAboutNonMustSupport; - private boolean showMessagesFromReferences; - private BestPracticeWarningLevel bpWarnings; - private String validationLanguage; - private boolean baseOnly; - - private List extensionDomains = new ArrayList(); - - private IdStatus resourceIdRule; - private boolean allowXsiLocation; - - // used during the build process to keep the overall volume of messages down - private boolean suppressLoincSnomedMessages; - - // time tracking - private long overall = 0; - private long txTime = 0; - private long sdTime = 0; - private long loadTime = 0; - private long fpeTime = 0; - - private boolean noBindingMsgSuppressed; - private boolean debug; - private Map fetchCache = new HashMap<>(); - private HashMap resourceTracker = new HashMap<>(); - private IValidatorResourceFetcher fetcher; - long time = 0; - private IEvaluationContext externalHostServices; - private boolean noExtensibleWarnings; - private String serverBase; - - private EnableWhenEvaluator myEnableWhenEvaluator = new EnableWhenEvaluator(); - private String executionId; - private XVerExtensionManager xverManager; - private IValidationProfileUsageTracker tracker; - private ValidatorHostServices validatorServices; - private boolean assumeValidRestReferences; - private boolean allowExamples; - - public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { - super(); - this.context = theContext; - this.externalHostServices = hostServices; - fpe = new FHIRPathEngine(context); - validatorServices = new ValidatorHostServices(); - fpe.setHostServices(validatorServices); - if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0")) - fpe.setLegacyMode(true); - source = Source.InstanceValidator; - } + private class ValidatorHostServices implements IEvaluationContext { @Override - public boolean isNoExtensibleWarnings() { - return noExtensibleWarnings; - } - - @Override - public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings) { - this.noExtensibleWarnings = noExtensibleWarnings; - return this; - } - - @Override - public boolean isShowMessagesFromReferences() { - return showMessagesFromReferences; - } - - @Override - public void setShowMessagesFromReferences(boolean showMessagesFromReferences) { - this.showMessagesFromReferences = showMessagesFromReferences; - } - - @Override - public boolean isNoInvariantChecks() { - return noInvariantChecks; - } - - @Override - public IResourceValidator setNoInvariantChecks(boolean value) { - this.noInvariantChecks = value; - return this; - } - - public IValidatorResourceFetcher getFetcher() { - return this.fetcher; - } - - public IResourceValidator setFetcher(IValidatorResourceFetcher value) { - this.fetcher = value; - return this; - } - - public IValidationProfileUsageTracker getTracker() { - return this.tracker; - } - - public IResourceValidator setTracker(IValidationProfileUsageTracker value) { - this.tracker = value; - return this; - } - - - public boolean isHintAboutNonMustSupport() { - return hintAboutNonMustSupport; - } - - public void setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) { - this.hintAboutNonMustSupport = hintAboutNonMustSupport; - } - - public boolean isAssumeValidRestReferences() { - return this.assumeValidRestReferences; - } - - public void setAssumeValidRestReferences(boolean value) { - this.assumeValidRestReferences = value; - } - - public boolean isAllowExamples() { - return this.allowExamples; - } - - public void setAllowExamples(boolean value) { - this.allowExamples = value; - } - - - private boolean allowUnknownExtension(String url) { - if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression")) - // Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with - return true; - for (String s : extensionDomains) - if (url.startsWith(s)) - return true; - return anyExtensionsAllowed; - } - - private boolean isKnownExtension(String url) { - // Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with - if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION)) - return true; - for (String s : extensionDomains) - if (url.startsWith(s)) - return true; - return false; - } - - private void bpCheck(List errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) { - if (bpWarnings != null) { - switch (bpWarnings) { - case Error: - rule(errors, invalid, line, col, literalPath, test, message); - break; - case Warning: - warning(errors, invalid, line, col, literalPath, test, message); - break; - case Hint: - hint(errors, invalid, line, col, literalPath, test, message); - break; - default: // do nothing - break; - } - } - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format) throws FHIRException { - return validate(appContext, errors, stream, format, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, stream, format, profiles); - } - - private StructureDefinition getSpecifiedProfile(String profile) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); - if (sd == null) { - throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); - } - return sd; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, List profiles) throws FHIRException { - ParserBase parser = Manager.makeParser(context, format); - if (parser instanceof XmlParser) - ((XmlParser) parser).setAllowXsiLocation(allowXsiLocation); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(stream); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource) throws FHIRException { - return validate(appContext, errors, resource, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, resource, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, List profiles) throws FHIRException { - long t = System.nanoTime(); - Element e; - try { - e = new ObjectConverter(context).convert(resource); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element) throws FHIRException { - return validate(appContext, errors, element, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, element, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, List profiles) throws FHIRException { - XmlParser parser = new XmlParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(element); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document) throws FHIRException { - return validate(appContext, errors, document, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, document, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, List profiles) throws FHIRException { - XmlParser parser = new XmlParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e; - try { - e = parser.parse(document); - } catch (IOException e1) { - throw new FHIRException(e1); - } - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object) throws FHIRException { - return validate(appContext, errors, object, new ArrayList<>()); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - return validate(appContext, errors, object, profiles); - } - - @Override - public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, List profiles) throws FHIRException { - JsonParser parser = new JsonParser(context); - parser.setupValidation(ValidationPolicy.EVERYTHING, errors); - long t = System.nanoTime(); - Element e = parser.parse(object); - loadTime = System.nanoTime() - t; - if (e != null) - validate(appContext, errors, e, profiles); - return e; - } - - @Override - public void validate(Object appContext, List errors, Element element) throws FHIRException { - validate(appContext, errors, element, new ArrayList<>()); - } - - @Override - public void validate(Object appContext, List errors, Element element, String profile) throws FHIRException { - ArrayList profiles = new ArrayList<>(); - if (profile != null) { - profiles.add(getSpecifiedProfile(profile)); - } - validate(appContext, errors, element, profiles); - } - - @Override - public void validate(Object appContext, List errors, Element element, List profiles) throws FHIRException { - // this is the main entry point; all the other public entry points end up here coming here... - // so the first thing to do is to clear the internal state - fetchCache.clear(); - fetchCache.put(element.fhirType() + "/" + element.getIdBase(), element); - resourceTracker.clear(); - executionId = UUID.randomUUID().toString(); - baseOnly = profiles.isEmpty(); - - long t = System.nanoTime(); - if (profiles == null || profiles.isEmpty()) { - validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(element)); - } else { - for (StructureDefinition defn : profiles) { - validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(element)); - } - } - if (hintAboutNonMustSupport) { - checkElementUsage(errors, element, new NodeStack(element)); - } - overall = System.nanoTime() - t; - } - - private void checkElementUsage(List errors, Element element, NodeStack stack) { - String elementUsage = element.getUserString("elementSupported"); - hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), - "The element " + element.getName() + " is not marked as 'mustSupport' in the profile " + element.getProperty().getStructure().getUrl() + ". Consider not using the element, or marking the element as must-Support in the profile"); - - if (element.hasChildren()) { - String prevName = ""; - int elementCount = 0; - for (Element ce : element.getChildren()) { - if (ce.getName().equals(prevName)) - elementCount++; - else { - elementCount = 1; - prevName = ce.getName(); - } - checkElementUsage(errors, ce, stack.push(ce, elementCount, null, null)); - } - } - } - - private boolean check(String v1, String v2) { - return v1 == null ? Utilities.noString(v1) : v1.equals(v2); - } - - private void checkAddress(List errors, String path, Element focus, Address fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - checkFixedValue(errors, path + ".city", focus.getNamedChild("city"), fixed.getCityElement(), fixedSource, "city", focus, pattern); - checkFixedValue(errors, path + ".state", focus.getNamedChild("state"), fixed.getStateElement(), fixedSource, "state", focus, pattern); - checkFixedValue(errors, path + ".country", focus.getNamedChild("country"), fixed.getCountryElement(), fixedSource, "country", focus, pattern); - checkFixedValue(errors, path + ".zip", focus.getNamedChild("zip"), fixed.getPostalCodeElement(), fixedSource, "postalCode", focus, pattern); - - List lines = new ArrayList(); - focus.getNamedChildren("line", lines); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(), - "Expected " + Integer.toString(fixed.getLine().size()) + " but found " + Integer.toString(lines.size()) + " line elements")) { - for (int i = 0; i < lines.size(); i++) - checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); - } - } - - private void checkAttachment(List errors, String path, Element focus, Attachment fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".contentType", focus.getNamedChild("contentType"), fixed.getContentTypeElement(), fixedSource, "contentType", focus, pattern); - checkFixedValue(errors, path + ".language", focus.getNamedChild("language"), fixed.getLanguageElement(), fixedSource, "language", focus, pattern); - checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); - checkFixedValue(errors, path + ".url", focus.getNamedChild("url"), fixed.getUrlElement(), fixedSource, "url", focus, pattern); - checkFixedValue(errors, path + ".size", focus.getNamedChild("size"), fixed.getSizeElement(), fixedSource, "size", focus, pattern); - checkFixedValue(errors, path + ".hash", focus.getNamedChild("hash"), fixed.getHashElement(), fixedSource, "hash", focus, pattern); - checkFixedValue(errors, path + ".title", focus.getNamedChild("title"), fixed.getTitleElement(), fixedSource, "title", focus, pattern); - } - - // public API - private boolean checkCode(List errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) 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(new ValidationOptions(stack.workingLang), system, code, checkDisplay ? display : null); - txTime = txTime + (System.nanoTime() - t); - if (s == null) - return true; - if (s.isOk()) { - if (s.getMessage() != null) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - return true; - } - if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else if (s.getSeverity() == IssueSeverity.INFORMATION) - txHint(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else if (s.getSeverity() == IssueSeverity.WARNING) - txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); - else - return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage() + " for '" + system + "#" + code + "'"); - return true; - } else if (system.startsWith("http://hl7.org/fhir")) { - if (Utilities.existsInList(system, "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/srt")) - return true; // else don't check these (for now) - else if (system.startsWith("http://hl7.org/fhir/test")) - return true; // we don't validate these - else { - CodeSystem cs = getCodeSystem(system); - if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System '" + system + "'")) { - ConceptDefinitionComponent def = getCodeDefinition(cs, code); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")")) - return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'"); - } - return false; - } - } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - return true; // no checks in this case - } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system); - return false; - } else { - try { - if (context.fetchResourceWithException(ValueSet.class, system) != null) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: " + system + " - cannot use a value set URI as a system"); - // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. - } - hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '" + system + "' is unknown so the code cannot be validated"); - return true; - } catch (Exception e) { - return true; - } - } - } - - private boolean startsWithButIsNot(String system, String... uri) { - for (String s : uri) - if (!system.equals(s) && system.startsWith(s)) - return true; - return false; - } - - - 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, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - List codings = new ArrayList(); - focus.getNamedChildren("coding", codings); - 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 allErrorsFixed = new ArrayList<>(); - List errorsFixed; - for (int j = 0; j < codings.size() && !found; ++j) { - errorsFixed = new ArrayList<>(); - checkFixedValue(errorsFixed, path + ".coding", codings.get(j), fixedCoding, fixedSource, "coding", focus, pattern); - if (!hasErrors(errorsFixed)) { - found = true; - } else { - errorsFixed - .stream() - .filter(t -> t.getLevel().ordinal() >= IssueSeverity.ERROR.ordinal()) - .forEach(t -> allErrorsFixed.add(t)); - } - } - if (!found) { - // The argonaut DSTU2 labs profile requires userSelected=false on the category.coding and this - // needs to produce an understandable error message - String message = "Expected CodeableConcept " + (pattern ? "pattern" : "fixed value") + " not found for" + - " system: " + fixedCoding.getSystemElement().asStringValue() + - " code: " + fixedCoding.getCodeElement().asStringValue() + - " display: " + fixedCoding.getDisplayElement().asStringValue(); - if (fixedCoding.hasUserSelected()) { - message += " userSelected: " + fixedCoding.getUserSelected(); - } - message += " - Issues: " + allErrorsFixed; - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, false, message); - } - } - } - } 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), fixedSource, "coding", focus); - } - } - } - - private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) { - 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)")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); - else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); - } - } else { - long t = System.nanoTime(); - - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); - 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"), cc, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - 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"), cc, stack); - if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); - } - } - } - } else if (vr.getMessage() != null) { - res = false; - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - res = false; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).noCheckValueSetMembership(), nextCoding, valueset); - if (vr.getSeverity() != null) { - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else if (vr.getSeverity() == IssueSeverity.WARNING) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } - } - } - } - } - txTime = txTime + (System.nanoTime() - t); - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - return res; - } - - private boolean checkTerminologyCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { - 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)")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - CodeableConcept cc = convertToCodeableConcept(element, logical); - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code must be provided from the value set " + describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + " (max value set " + valueset.getUrl() + ")"); - else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code should be provided from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ")"); - } - } else { - long t = System.nanoTime(); - - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = " + vr.getErrorClass().toString() + ")"); - 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"), cc, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = " + vr.getErrorClass().toString() + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code is recommended to come from this value set (class = " + vr.getErrorClass().toString() + ")"); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - 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"), cc, stack); - if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = " + ccSummary(cc) + ")"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is recommended to come from this value set) (codes = " + ccSummary(cc) + ")"); - } - } - } - } else if (vr.getMessage() != null) { - res = false; - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - res = false; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - String nextCode = nextCoding.getCode(); - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); - if (!vr.isOk()) { - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); - } - } - } - } - txTime = txTime + (System.nanoTime() - t); - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept"); - } - // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. - if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { - checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - return res; - } - - private void checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { - Coding c = convertToCoding(element, logical); - String code = c.getCode(); - String system = c.getSystem(); - String display = c.getDisplay(); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); - - 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, checkDisplay, stack)) - 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")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); - else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); - 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"), c, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - 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"), c, stack); - else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set" + (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); - } - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); - } - } - } - - private CodeableConcept convertToCodeableConcept(Element element, StructureDefinition logical) { - CodeableConcept res = new CodeableConcept(); - for (ElementDefinition ed : logical.getSnapshot().getElement()) { - if (Utilities.charCount(ed.getPath(), '.') == 1) { - List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); - for (String m : maps) { - String name = tail(ed.getPath()); - List list = new ArrayList<>(); - element.getNamedChildren(name, list); - if (!list.isEmpty()) { - if ("Coding.code".equals(m)) { - res.getCodingFirstRep().setCode(list.get(0).primitiveValue()); - } else if ("Coding.system[fmt:OID]".equals(m)) { - String oid = list.get(0).primitiveValue(); - String url = context.oid2Uri(oid); - if (url != null) { - res.getCodingFirstRep().setSystem(url); - } else { - res.getCodingFirstRep().setSystem("urn:oid:" + oid); - } - } else if ("Coding.version".equals(m)) { - res.getCodingFirstRep().setVersion(list.get(0).primitiveValue()); - } else if ("Coding.display".equals(m)) { - res.getCodingFirstRep().setDisplay(list.get(0).primitiveValue()); - } else if ("CodeableConcept.text".equals(m)) { - res.setText(list.get(0).primitiveValue()); - } else if ("CodeableConcept.coding".equals(m)) { - StructureDefinition c = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode()); - for (Element e : list) { - res.addCoding(convertToCoding(e, c)); - } - } - } - } - } - } - return res; - } - - private Coding convertToCoding(Element element, StructureDefinition logical) { - Coding res = new Coding(); - for (ElementDefinition ed : logical.getSnapshot().getElement()) { - if (Utilities.charCount(ed.getPath(), '.') == 1) { - List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); - for (String m : maps) { - String name = tail(ed.getPath()); - List list = new ArrayList<>(); - element.getNamedChildren(name, list); - if (!list.isEmpty()) { - if ("Coding.code".equals(m)) { - res.setCode(list.get(0).primitiveValue()); - } else if ("Coding.system[fmt:OID]".equals(m)) { - String oid = list.get(0).primitiveValue(); - String url = context.oid2Uri(oid); - if (url != null) { - res.setSystem(url); - } else { - res.setSystem("urn:oid:" + oid); - } - } else if ("Coding.version".equals(m)) { - res.setVersion(list.get(0).primitiveValue()); - } else if ("Coding.display".equals(m)) { - res.setDisplay(list.get(0).primitiveValue()); - } - } - } - } - } - return res; - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided could be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (codes = " + ccSummary(cc) + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + ", and a code from this value set is required) (code = " + c.getSystem() + "#" + c.getCode() + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { - // TODO Auto-generated method stub - ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found by validator")) { - try { - long t = System.nanoTime(); - ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); - txTime = txTime + (System.nanoTime() - t); - if (!vr.isOk()) { - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided could not be validated against the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), (error = " + vr.getMessage() + ")"); - else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The code provided is not in the maximum value set " + describeReference(maxVSUrl) + " (" + valueset.getUrl() + "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating CodeableConcept using maxValueSet"); - } - } - } - - private String ccSummary(CodeableConcept cc) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (Coding c : cc.getCoding()) - b.append(c.getSystem() + "#" + c.getCode()); - return b.toString(); - } - - private void checkCoding(List errors, String path, Element focus, Coding fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".version", focus.getNamedChild("version"), fixed.getVersionElement(), fixedSource, "version", focus, pattern); - checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); - checkFixedValue(errors, path + ".display", focus.getNamedChild("display"), fixed.getDisplayElement(), fixedSource, "display", focus, pattern); - checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), fixedSource, "userSelected", focus, pattern); - } - - private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) { - String code = element.getNamedChildValue("code"); - String system = element.getNamedChildValue("system"); - String display = element.getNamedChildValue("display"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference"); - - 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, checkDisplay, stack)) - 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")) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(binding.getValueSet()) + " not found by validator")) { - try { - Coding c = ObjectConverter.readAsCoding(element); - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server"); - else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code from this value set is required"); - 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"), c, stack); - else if (!noExtensibleWarnings) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code"); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set"); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is required from this value set. " + getErrorMessage(vr.getMessage())); - 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"), c, stack); - else - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code should come from this value set unless it has no suitable code. " + getErrorMessage(vr.getMessage())); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet(), valueset) + ", and a code is recommended to come from this value set. " + getErrorMessage(vr.getMessage())); - } - } - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding"); - } - } - } else if (binding.hasValueSet()) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding by URI reference cannot be checked"); - } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Binding for path " + path + " has no source, so can't be checked"); - } - } - } - } catch (Exception e) { - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Error " + e.getMessage() + " validating Coding: " + e.toString()); - } - } - } - - private boolean isValueSet(String url) { - try { - ValueSet vs = context.fetchResourceWithException(ValueSet.class, url); - return vs != null; - } catch (Exception e) { - return false; - } - } - - private void checkContactPoint(List errors, String path, Element focus, ContactPoint fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); - - } - - private StructureDefinition checkExtension(ValidatorHostContext hostContext, List errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { - String url = element.getNamedChildValue("url"); - boolean isModifier = element.getName().equals("modifierExtension"); - - long t = System.nanoTime(); - StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null; - sdTime = sdTime + (System.nanoTime() - t); - if (ex == null) { - if (xverManager == null) { - xverManager = new XVerExtensionManager(context); - } - if (xverManager.matchingUrl(url)) { - switch (xverManager.status(url)) { - case BadVersion: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalid Version \"" + xverManager.getVersion(url) + "\")"); - break; - case Unknown: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (invalid Element id \"" + xverManager.getElementId(url) + "\")"); - break; - case Invalid: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' is not valid (Element id \"" + xverManager.getElementId(url) + "\" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions)"); - break; - case Valid: - ex = xverManager.makeDefinition(url); - context.generateSnapshot(ex); - context.cacheResource(ex); - break; - default: - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, "Extension url '" + url + "' evaluation state illegal"); - break; - } - } else if (extensionUrl != null && !isAbsolute(url)) { - if (extensionUrl.equals(profile.getUrl())) { - rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), "Sub-extension url '" + url + "' is not defined by the Extension " + profile.getUrl()); - } - } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), "The extension " + url + " is unknown, and not allowed here")) { - hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), "Unknown extension " + url); - } - } - if (ex != null) { - trackUsage(ex, hostContext, element); - if (def.getIsModifier()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not"); - } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is"); - } - // two questions - // 1. can this extension be used here? - checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); - - if (isModifier) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must be used as a modifierExtension"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), - "The Extension '" + url + "' must not be used as an extension (it's a modifierExtension)"); - - // check the type of the extension: - Set allowedTypes = listExtensionTypes(ex); - String actualType = getExtensionType(element); - if (actualType == null) - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), "The Extension '" + url + "' definition is for a simple extension, so it must contain a value, not extensions"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, 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, true, url); - - } - return ex; - } - - private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { - for (ElementDefinition ed : profile.getSnapshot().getElement()) { - if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { - return true; - } - } - return false; - } - - private String getExtensionType(Element element) { - for (Element e : element.getChildren()) { - if (e.getName().startsWith("value")) { - String tn = e.getName().substring(5); - String ltn = Utilities.uncapitalize(tn); - if (isPrimitiveType(ltn)) - return ltn; - else - return tn; - } - } + public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (externalHostServices != null) + return externalHostServices.resolveConstant(c.getAppContext(), name, beforeContext); + else return null; } - private Set listExtensionTypes(StructureDefinition ex) { - ElementDefinition vd = null; - for (ElementDefinition ed : ex.getSnapshot().getElement()) { - if (ed.getPath().startsWith("Extension.value")) { - vd = ed; - break; - } - } - Set res = new HashSet(); - if (vd != null && !"0".equals(vd.getMax())) { - for (TypeRefComponent tr : vd.getType()) { - res.add(tr.getWorkingCode()); - } - } + @Override + public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (externalHostServices != null) + return externalHostServices.resolveConstantType(c.getAppContext(), name); + else + return null; + } + + @Override + public boolean log(String argument, List focus) { + if (externalHostServices != null) + return externalHostServices.log(argument, focus); + else + return false; + } + + @Override + public FunctionDetails resolveFunction(String functionName) { + throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); + } + + @Override + public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); + } + + @Override + public List executeFunction(Object appContext, String functionName, List> parameters) { + throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); + } + + @Override + public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + ValidatorHostContext c = (ValidatorHostContext) appContext; + + if (refContext != null && refContext.hasUserData("validator.bundle.resolution")) { + return (Base) refContext.getUserData("validator.bundle.resolution"); + } + + if (c.getAppContext() instanceof Element) { + Element bnd = (Element) c.getAppContext(); + Base res = resolveInBundle(url, bnd); + if (res != null) + return res; + } + Base res = resolveInBundle(url, c.getResource()); + if (res != null) return res; + res = resolveInBundle(url, c.getContainer()); + if (res != null) + return res; + + if (externalHostServices != null) + return externalHostServices.resolveReference(c.getAppContext(), url, refContext); + else if (fetcher != null) + try { + return fetcher.fetch(c.getAppContext(), url); + } catch (IOException e) { + throw new FHIRException(e); + } + else + throw new Error("Not done yet - resolve " + url + " locally (2)"); + } - private boolean checkExtensionContext(List errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext) { - String extUrl = definition.getUrl(); - boolean ok = false; - CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder(); - List plist = new ArrayList<>(); - plist.add(stripIndexes(stack.getLiteralPath())); - for (String s : stack.getLogicalPaths()) { - String p = stripIndexes(s); - // all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition.... - if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) { - return true; - } - plist.add(p); - + public Base resolveInBundle(String url, Element bnd) { + if (bnd == null) + return null; + if (bnd.fhirType().equals("Bundle")) { + for (Element be : bnd.getChildrenByName("entry")) { + Element res = be.getNamedChild("resource"); + if (res != null) { + String fullUrl = be.getChildValue("fullUrl"); + String rt = res.fhirType(); + String id = res.getChildValue("id"); + if (url.equals(fullUrl)) + return res; + if (url.equals(rt + "/" + id)) + return res; + } } + } + return null; + } - for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { - if (ok) { - break; - } - if (ctxt.getType() == ExtensionContextType.ELEMENT) { - String en = ctxt.getExpression(); - contexts.append("e:" + en); - if ("Element".equals(en)) { - ok = true; - } else if (en.equals("Resource") && container.isResource()) { - ok = true; - } - for (String p : plist) { - if (ok) { - break; - } - if (p.equals(en)) { - ok = true; - } else { - String pn = p; - String pt = ""; - if (p.contains(".")) { - pn = p.substring(0, p.indexOf(".")); - pt = p.substring(p.indexOf(".")); - } - StructureDefinition sd = context.fetchTypeDefinition(pn); - while (sd != null) { - if ((sd.getType() + pt).equals(en)) { - ok = true; - break; - } - if (sd.getBaseDefinition() != null) { - sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); - } else { - sd = null; - } - } - } - } - } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { - contexts.append("x:" + ctxt.getExpression()); - NodeStack estack = stack.parent; - if (estack != null && estack.getElement().fhirType().equals("Extension")) { - String ext = estack.element.getNamedChildValue("url"); - if (ctxt.getExpression().equals(ext)) { - ok = true; - } - } - } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { - contexts.append("p:" + ctxt.getExpression()); - // The context is all elements that match the FHIRPath query found in the expression. - List res = fpe.evaluate(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(ctxt.getExpression())); - if (res.contains(container)) { - ok = true; - } - } else { - throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); - } + @Override + public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + ValidatorHostContext ctxt = (ValidatorHostContext) appContext; + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + if (sd == null) { + throw new FHIRException("Unable to resolve " + url); + } + InstanceValidator self = InstanceValidator.this; + List valerrors = new ArrayList(); + if (item instanceof Resource) { + try { + Element e = new ObjectConverter(context).convert((Resource) item); + self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); + } catch (IOException e1) { + throw new FHIRException(e1); } - if (!ok) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, "The extension " + extUrl + " is not allowed to be used at this point (allowed = " + contexts.toString() + "; this element is [" + plist.toString() + ")"); - return false; + } else if (item instanceof Element) { + Element e = (Element) item; + if (e.isResource()) { + self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); } else { - if (definition.hasContextInvariant()) { - for (StringType s : definition.getContextInvariant()) { - if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { - rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, - "The extension " + extUrl + " is not allowed to be used at this point (based on context invariant '" + s.getValue() + "')"); - return false; - } - } - } - return true; + throw new FHIRException("Not supported yet"); } + } else + throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); + boolean ok = true; + List record = new ArrayList<>(); + for (ValidationMessage v : valerrors) { + ok = ok && !v.getLevel().isError(); + if (v.getLevel().isError() || v.isSlicingHint()) { + record.add(v); + } + } + if (!ok && !record.isEmpty()) { + ctxt.sliceNotes(url, record); + } + return ok; } - private List fixContexts(String extUrl, List list) { - List res = new ArrayList<>(); - for (StructureDefinitionContextComponent ctxt : list) { - res.add(ctxt.copy()); - } - if ("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type".equals(extUrl)) { - list.get(0).setExpression("ElementDefinition.type"); - } - if ("http://hl7.org/fhir/StructureDefinition/regex".equals(extUrl)) { - list.get(1).setExpression("ElementDefinition.type"); - } - return list; - } - - private String stripIndexes(String path) { - boolean skip = false; - StringBuilder b = new StringBuilder(); - for (char c : path.toCharArray()) { - if (skip) { - if (c == ']') { - skip = false; - } - } else if (c == '[') { - skip = true; - } else { - b.append(c); - } - } - return b.toString(); - } - - private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent) { - checkFixedValue(errors, path, focus, fixed, fixedSource, propName, parent, false); - } - - @SuppressWarnings("rawtypes") - private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) { - if ((fixed == null || fixed.isEmpty()) && focus == null) { - ; // this is all good - } else if ((fixed == null || fixed.isEmpty()) && focus != null) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern, "The element " + focus.getName() + " is present in the instance but not allowed in the applicable " + (pattern ? "pattern" : "fixed value") + " specified in profile"); - } else if (fixed != null && !fixed.isEmpty() && focus == null) { - rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false, "Missing element '" + propName + "' - required by fixed value assigned in profile " + fixedSource); - } else { - String value = focus.primitiveValue(); - if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.CodeType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.StringType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.StringType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.UriType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UriType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DateType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.OidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.OidType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.UuidType) fixed).getValue() + "'"); - else if (fixed instanceof org.hl7.fhir.r5.model.IdType) - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value), - "Value is '" + value + "' but must be '" + ((org.hl7.fhir.r5.model.IdType) fixed).getValue() + "'"); - else if (fixed instanceof Quantity) - checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); - else if (fixed instanceof Address) - checkAddress(errors, path, focus, (Address) fixed, fixedSource, pattern); - else if (fixed instanceof ContactPoint) - checkContactPoint(errors, path, focus, (ContactPoint) fixed, fixedSource, pattern); - else if (fixed instanceof Attachment) - checkAttachment(errors, path, focus, (Attachment) fixed, fixedSource, pattern); - else if (fixed instanceof Identifier) - checkIdentifier(errors, path, focus, (Identifier) fixed, fixedSource, pattern); - else if (fixed instanceof Coding) - checkCoding(errors, path, focus, (Coding) fixed, fixedSource, pattern); - else if (fixed instanceof HumanName) - checkHumanName(errors, path, focus, (HumanName) fixed, fixedSource, pattern); - else if (fixed instanceof CodeableConcept) - checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed, fixedSource, pattern); - else if (fixed instanceof Timing) - checkTiming(errors, path, focus, (Timing) fixed, fixedSource, pattern); - else if (fixed instanceof Period) - checkPeriod(errors, path, focus, (Period) fixed, fixedSource, pattern); - else if (fixed instanceof Range) - checkRange(errors, path, focus, (Range) fixed, fixedSource, pattern); - else if (fixed instanceof Ratio) - checkRatio(errors, path, focus, (Ratio) fixed, fixedSource, pattern); - else if (fixed instanceof SampledData) - checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); - + @Override + public ValueSet resolveValueSet(Object appContext, String url) { + ValidatorHostContext c = (ValidatorHostContext) appContext; + if (c.getProfile() != null && url.startsWith("#")) { + for (Resource r : c.getProfile().getContained()) { + if (r.getId().equals(url.substring(1))) { + if (r instanceof ValueSet) + return (ValueSet) r; else - rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false, "Unhandled fixed value type " + fixed.getClass().getName()); - List extensions = new ArrayList(); - focus.getNamedChildren("extension", extensions); - if (fixed.getExtension().size() == 0) { - rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0, "No extensions allowed, as the specified fixed value doesn't contain any extensions"); - } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(), - "Extensions count mismatch: expected " + Integer.toString(fixed.getExtension().size()) + " but found " + Integer.toString(extensions.size()))) { - for (Extension e : fixed.getExtension()) { - Element ex = getExtensionByUrl(extensions, e.getUrl()); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, "Extension count mismatch: unable to find extension: " + e.getUrl())) { - checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); - } - } - } + throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); + } } + return null; + } + return context.fetchResource(ValueSet.class, url); } - private void checkHumanName(List errors, String path, Element focus, HumanName fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + } - List parts = new ArrayList(); - focus.getNamedChildren("family", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), - "Expected " + (fixed.hasFamily() ? "1" : "0") + " but found " + Integer.toString(parts.size()) + " family elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); - } - focus.getNamedChildren("given", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), - "Expected " + Integer.toString(fixed.getGiven().size()) + " but found " + Integer.toString(parts.size()) + " given elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); - } - focus.getNamedChildren("prefix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), - "Expected " + Integer.toString(fixed.getPrefix().size()) + " but found " + Integer.toString(parts.size()) + " prefix elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); - } - focus.getNamedChildren("suffix", parts); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), - "Expected " + Integer.toString(fixed.getSuffix().size()) + " but found " + Integer.toString(parts.size()) + " suffix elements")) { - for (int i = 0; i < parts.size(); i++) - checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); - } + private IWorkerContext context; + private FHIRPathEngine fpe; + + // configuration items + private CheckDisplayOption checkDisplay; + private boolean anyExtensionsAllowed; + private boolean errorForUnknownProfiles; + private boolean noInvariantChecks; + private boolean noTerminologyChecks; + private boolean hintAboutNonMustSupport; + private boolean showMessagesFromReferences; + private BestPracticeWarningLevel bpWarnings; + private String validationLanguage; + private boolean baseOnly; + + private List extensionDomains = new ArrayList(); + + private IdStatus resourceIdRule; + private boolean allowXsiLocation; + + // used during the build process to keep the overall volume of messages down + private boolean suppressLoincSnomedMessages; + + // time tracking + private long overall = 0; + private long txTime = 0; + private long sdTime = 0; + private long loadTime = 0; + private long fpeTime = 0; + + private boolean noBindingMsgSuppressed; + private boolean debug; + private Map fetchCache = new HashMap<>(); + private HashMap resourceTracker = new HashMap<>(); + private IValidatorResourceFetcher fetcher; + long time = 0; + private IEvaluationContext externalHostServices; + private boolean noExtensibleWarnings; + private String serverBase; + + private EnableWhenEvaluator myEnableWhenEvaluator = new EnableWhenEvaluator(); + private String executionId; + private XVerExtensionManager xverManager; + private IValidationProfileUsageTracker tracker; + private ValidatorHostServices validatorServices; + private boolean assumeValidRestReferences; + private boolean allowExamples; + + public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { + super(theContext); + this.context = theContext; + this.externalHostServices = hostServices; + fpe = new FHIRPathEngine(context); + validatorServices = new ValidatorHostServices(); + fpe.setHostServices(validatorServices); + if (theContext.getVersion().startsWith("3.0") || theContext.getVersion().startsWith("1.0")) + fpe.setLegacyMode(true); + source = Source.InstanceValidator; + } + + @Override + public boolean isNoExtensibleWarnings() { + return noExtensibleWarnings; + } + + @Override + public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings) { + this.noExtensibleWarnings = noExtensibleWarnings; + return this; + } + + @Override + public boolean isShowMessagesFromReferences() { + return showMessagesFromReferences; + } + + @Override + public void setShowMessagesFromReferences(boolean showMessagesFromReferences) { + this.showMessagesFromReferences = showMessagesFromReferences; + } + + @Override + public boolean isNoInvariantChecks() { + return noInvariantChecks; + } + + @Override + public IResourceValidator setNoInvariantChecks(boolean value) { + this.noInvariantChecks = value; + return this; + } + + public IValidatorResourceFetcher getFetcher() { + return this.fetcher; + } + + public IResourceValidator setFetcher(IValidatorResourceFetcher value) { + this.fetcher = value; + return this; + } + + public IValidationProfileUsageTracker getTracker() { + return this.tracker; + } + + public IResourceValidator setTracker(IValidationProfileUsageTracker value) { + this.tracker = value; + return this; + } + + + public boolean isHintAboutNonMustSupport() { + return hintAboutNonMustSupport; + } + + public void setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) { + this.hintAboutNonMustSupport = hintAboutNonMustSupport; + } + + public boolean isAssumeValidRestReferences() { + return this.assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean value) { + this.assumeValidRestReferences = value; + } + + public boolean isAllowExamples() { + return this.allowExamples; + } + + public void setAllowExamples(boolean value) { + this.allowExamples = value; + } + + + private boolean allowUnknownExtension(String url) { + if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression")) + // Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with + return true; + for (String s : extensionDomains) + if (url.startsWith(s)) + return true; + return anyExtensionsAllowed; + } + + private boolean isKnownExtension(String url) { + // Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with + if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION)) + return true; + for (String s : extensionDomains) + if (url.startsWith(s)) + return true; + return false; + } + + private void bpCheck(List errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) { + if (bpWarnings != null) { + switch (bpWarnings) { + case Error: + rule(errors, invalid, line, col, literalPath, test, message); + break; + case Warning: + warning(errors, invalid, line, col, literalPath, test, message); + break; + case Hint: + hint(errors, invalid, line, col, literalPath, test, message); + break; + default: // do nothing + break; + } } + } - private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { - String system = element.getNamedChildValue("system"); - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Identifier.system must be an absolute reference, not a local reference"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format) throws FHIRException { + return validate(appContext, errors, stream, format, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); } + return validate(appContext, errors, stream, format, profiles); + } - private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); - checkFixedValue(errors, path + ".type", focus.getNamedChild("type"), fixed.getType(), fixedSource, "type", focus, pattern); - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); - checkFixedValue(errors, path + ".assigner", focus.getNamedChild("assigner"), fixed.getAssigner(), fixedSource, "assigner", focus, pattern); + private StructureDefinition getSpecifiedProfile(String profile) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); + if (sd == null) { + throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); } + return sd; + } - private void checkPeriod(List errors, String path, Element focus, Period fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".start", focus.getNamedChild("start"), fixed.getStartElement(), fixedSource, "start", focus, pattern); - checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), fixedSource, "end", focus, pattern); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, InputStream stream, FhirFormat format, List profiles) throws FHIRException { + ParserBase parser = Manager.makeParser(context, format); + if (parser instanceof XmlParser) + ((XmlParser) parser).setAllowXsiLocation(allowXsiLocation); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(stream); + } catch (IOException e1) { + throw new FHIRException(e1); } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } - private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { - if (isBlank(e.primitiveValue())) { - if (e.primitiveValue() == null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions"); - else if (e.primitiveValue().length() == 0) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "@value cannot be empty"); - else if (StringUtils.isWhitespace(e.primitiveValue())) - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types should not only be whitespace"); - return; - } - String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); - if (regex != null) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), "Element value '" + e.primitiveValue() + "' does not meet regex '" + regex + "'"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource) throws FHIRException { + return validate(appContext, errors, resource, new ArrayList<>()); + } - if (type.equals("boolean")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'"); - } - if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { - String url = e.primitiveValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), "URI values cannot start with oid:"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), "URI values cannot start with uuid:"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) - // work around an old invalid example in a core package - || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), "URI values cannot have whitespace('" + url + "')"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - - if (type.equals("oid")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), "OIDs must start with urn:oid:")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), "OIDs must be valid"); - } - if (type.equals("uuid")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:"); - try { - UUID.fromString(url.substring(8)); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid (" + ex.getMessage() + ")"); - } - } - - // now, do we check the URI target? - if (fetcher != null) { - boolean found; - try { - found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url); - } catch (IOException e1) { - found = false; - } - rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '" + url + "' does not resolve"); - } - } - if (type.equals("id")) { - // work around an old issue with ElementDefinition.id - if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), "id value '" + e.primitiveValue() + "' is not valid"); - } - } - if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, "@value cannot be empty")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), "value should not start or finish with whitespace"); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, "value is longer than permitted maximum length of 1 MB (1048576 bytes)")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - } - } - } - if (type.equals("dateTime")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue() - .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), - "Not a valid date time"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), "if a date has a time, it must have a timezone"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - try { - DateTimeType dt = new DateTimeType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date/time (" + ex.getMessage() + ")"); - } - } - if (type.equals("time")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue() - .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"), - "Not a valid time"); - try { - TimeType dt = new TimeType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid time (" + ex.getMessage() + ")"); - } - } - if (type.equals("date")) { - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), - "Not a valid date"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum value of " + context.getMaxLength()); - try { - DateType dt = new DateType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid date (" + ex.getMessage() + ")"); - } - } - if (type.equals("base64Binary")) { - String encoded = e.primitiveValue(); - if (isNotBlank(encoded)) { - /* - * Technically this is not bulletproof as some invalid base64 won't be caught, - * but I think it's good enough. The original code used Java8 Base64 decoder - * but I've replaced it with a regex for 2 reasons: - * 1. This code will run on any version of Java - * 2. This code doesn't actually decode, which is much easier on memory use for big payloads - */ - int charCount = 0; - for (int i = 0; i < encoded.length(); i++) { - char nextChar = encoded.charAt(i); - if (Character.isWhitespace(nextChar)) { - continue; - } - if (Character.isLetterOrDigit(nextChar)) { - charCount++; - } - if (nextChar == '/' || nextChar == '=' || nextChar == '+') { - charCount++; - } - } - - if (charCount > 0 && charCount % 4 != 0) { - String value = encoded.length() < 100 ? encoded : "(snip)"; - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "The value \"{0}\" is not a valid Base64 value", value); - } - } - } - if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer")) { - Integer v = new Integer(e.getValue()).intValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); - if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); - if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); - } - } - if (type.equals("integer64")) { - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is not a valid integer64")) { - Long v = new Long(e.getValue()).longValue(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v), "value is greater than permitted maximum value of " + (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v), "value is less than permitted minimum value of " + (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); - if (type.equals("unsignedInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, "value is less than permitted minimum value of 0"); - if (type.equals("positiveInt")) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, "value is less than permitted minimum value of 1"); - } - } - if (type.equals("decimal")) { - if (e.primitiveValue() != null) { - DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); - if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is not a valid decimal")) - warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, "The value '" + e.primitiveValue() + "' is outside the range of commonly/reasonably supported decimals"); - } - } - if (type.equals("instant")) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, - e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), - "The instant '" + e.primitiveValue() + "' is not valid (by regex)"); - warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), "The value '" + e.primitiveValue() + "' is outside the range of reasonable years - check for data entry error"); - try { - InstantType dt = new InstantType(e.primitiveValue()); - } catch (Exception ex) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Not a valid instant (" + ex.getMessage() + ")"); - } - } - - if (type.equals("code") && e.primitiveValue() != null) { - // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace - // other than single spaces in the contents - rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()), "The code '" + e.primitiveValue() + "' is not valid (whitespace rules)"); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength()); - } - - if (context.hasBinding() && e.primitiveValue() != null) { - checkPrimitiveBinding(errors, path, type, context, e, profile, node); - } - - if (type.equals("xhtml")) { - XhtmlNode xhtml = e.getXhtml(); - if (xhtml != null) { // if it is null, this is an error already noted in the parsers - // check that the namespace is there and correct. - String ns = xhtml.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); - // check that inner namespaces are all correct - checkInnerNS(errors, e, path, xhtml.getChildNodes()); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), "Wrong name on the XHTML ('" + ns + "') - must start with div"); - // check that no illegal elements and attributes have been used - checkInnerNames(errors, e, path, xhtml.getChildNodes()); - } - } - - if (context.hasFixed()) { - checkFixedValue(errors, path, e, context.getFixed(), profile.getUrl(), context.getSliceName(), null, false); - } - if (context.hasPattern()) { - checkFixedValue(errors, path, e, context.getPattern(), profile.getUrl(), context.getSliceName(), null, true); - } - - // for nothing to check + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); } + return validate(appContext, errors, resource, profiles); + } - private boolean isDefinitionURL(String url) { - return Utilities.existsInList(url, "http://hl7.org/fhirpath/System.Boolean", "http://hl7.org/fhirpath/System.String", "http://hl7.org/fhirpath/System.Integer", - "http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity"); + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Resource resource, List profiles) throws FHIRException { + long t = System.nanoTime(); + Element e; + try { + e = new ObjectConverter(context).convert(resource); + } catch (IOException e1) { + throw new FHIRException(e1); } + loadTime = System.nanoTime() - t; + validate(appContext, errors, e, profiles); + return e; + } - private void checkInnerNames(List errors, Element e, String path, List list) { - for (XhtmlNode node : list) { - if (node.getNodeType() == NodeType.Element) { - rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), - "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", - "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", - "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", - "code", "samp", "img", "map", "area" + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element) throws FHIRException { + return validate(appContext, errors, element, new ArrayList<>()); + } - ), "Illegal element name in the XHTML ('" + node.getName() + "')"); - for (String an : node.getAttributes().keySet()) { - boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, - "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", - // tables - "span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") || + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, element, profiles); + } - Utilities.existsInList(node.getName() + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite", - "a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src", - "img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape", - "area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border", - "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" - ); - if (!ok) - rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "Illegal attribute name in the XHTML ('" + an + "' on '" + node.getName() + "')"); - } - checkInnerNames(errors, e, path, node.getChildNodes()); - } + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, org.w3c.dom.Element element, List profiles) throws FHIRException { + XmlParser parser = new XmlParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(element); + } catch (IOException e1) { + throw new FHIRException(e1); + } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document) throws FHIRException { + return validate(appContext, errors, document, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, document, profiles); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, Document document, List profiles) throws FHIRException { + XmlParser parser = new XmlParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e; + try { + e = parser.parse(document); + } catch (IOException e1) { + throw new FHIRException(e1); + } + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object) throws FHIRException { + return validate(appContext, errors, object, new ArrayList<>()); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + return validate(appContext, errors, object, profiles); + } + + @Override + public org.hl7.fhir.r5.elementmodel.Element validate(Object appContext, List errors, JsonObject object, List profiles) throws FHIRException { + JsonParser parser = new JsonParser(context); + parser.setupValidation(ValidationPolicy.EVERYTHING, errors); + long t = System.nanoTime(); + Element e = parser.parse(object); + loadTime = System.nanoTime() - t; + if (e != null) + validate(appContext, errors, e, profiles); + return e; + } + + @Override + public void validate(Object appContext, List errors, Element element) throws FHIRException { + validate(appContext, errors, element, new ArrayList<>()); + } + + @Override + public void validate(Object appContext, List errors, Element element, String profile) throws FHIRException { + ArrayList profiles = new ArrayList<>(); + if (profile != null) { + profiles.add(getSpecifiedProfile(profile)); + } + validate(appContext, errors, element, profiles); + } + + @Override + public void validate(Object appContext, List errors, Element element, List profiles) throws FHIRException { + // this is the main entry point; all the other public entry points end up here coming here... + // so the first thing to do is to clear the internal state + fetchCache.clear(); + fetchCache.put(element.fhirType() + "/" + element.getIdBase(), element); + resourceTracker.clear(); + executionId = UUID.randomUUID().toString(); + baseOnly = profiles.isEmpty(); + + long t = System.nanoTime(); + if (profiles == null || profiles.isEmpty()) { + validateResource(new ValidatorHostContext(appContext, element), errors, element, element, null, resourceIdRule, new NodeStack(element)); + } else { + for (StructureDefinition defn : profiles) { + validateResource(new ValidatorHostContext(appContext, element), errors, element, element, defn, resourceIdRule, new NodeStack(element)); + } + } + if (hintAboutNonMustSupport) { + checkElementUsage(errors, element, new NodeStack(element)); + } + overall = System.nanoTime() - t; + } + + private void checkElementUsage(List errors, Element element, NodeStack stack) { + String elementUsage = element.getUserString("elementSupported"); + hint(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), elementUsage == null || elementUsage.equals("Y"), I18nConstants.MUSTSUPPORT_VAL_MUSTSUPPORT, element.getName(), element.getProperty().getStructure().getUrl()); + + if (element.hasChildren()) { + String prevName = ""; + int elementCount = 0; + for (Element ce : element.getChildren()) { + if (ce.getName().equals(prevName)) + elementCount++; + else { + elementCount = 1; + prevName = ce.getName(); } + checkElementUsage(errors, ce, stack.push(ce, elementCount, null, null)); + } } + } - private void checkInnerNS(List errors, Element e, String path, List list) { - for (XhtmlNode node : list) { - if (node.getNodeType() == NodeType.Element) { - String ns = node.getNsDecl(); - rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns), "Wrong namespace on the XHTML ('" + ns + "', should be '" + FormatUtilities.XHTML_NS + "')"); - checkInnerNS(errors, e, path, node.getChildNodes()); - } + private boolean check(String v1, String v2) { + return v1 == null ? Utilities.noString(v1) : v1.equals(v2); + } + + private void checkAddress(List errors, String path, Element focus, Address fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + checkFixedValue(errors, path + ".city", focus.getNamedChild("city"), fixed.getCityElement(), fixedSource, "city", focus, pattern); + checkFixedValue(errors, path + ".state", focus.getNamedChild("state"), fixed.getStateElement(), fixedSource, "state", focus, pattern); + checkFixedValue(errors, path + ".country", focus.getNamedChild("country"), fixed.getCountryElement(), fixedSource, "country", focus, pattern); + checkFixedValue(errors, path + ".zip", focus.getNamedChild("zip"), fixed.getPostalCodeElement(), fixedSource, "postalCode", focus, pattern); + + List lines = new ArrayList(); + focus.getNamedChildren("line", lines); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, lines.size() == fixed.getLine().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_ADDRESS_LINE, Integer.toString(fixed.getLine().size()), Integer.toString(lines.size()))) { + for (int i = 0; i < lines.size(); i++) + checkFixedValue(errors, path + ".coding", lines.get(i), fixed.getLine().get(i), fixedSource, "coding", focus, pattern); + } + } + + private void checkAttachment(List errors, String path, Element focus, Attachment fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".contentType", focus.getNamedChild("contentType"), fixed.getContentTypeElement(), fixedSource, "contentType", focus, pattern); + checkFixedValue(errors, path + ".language", focus.getNamedChild("language"), fixed.getLanguageElement(), fixedSource, "language", focus, pattern); + checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); + checkFixedValue(errors, path + ".url", focus.getNamedChild("url"), fixed.getUrlElement(), fixedSource, "url", focus, pattern); + checkFixedValue(errors, path + ".size", focus.getNamedChild("size"), fixed.getSizeElement(), fixedSource, "size", focus, pattern); + checkFixedValue(errors, path + ".hash", focus.getNamedChild("hash"), fixed.getHashElement(), fixedSource, "hash", focus, pattern); + checkFixedValue(errors, path + ".title", focus.getNamedChild("title"), fixed.getTitleElement(), fixedSource, "title", focus, pattern); + } + + // public API + private boolean checkCode(List errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) 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(new ValidationOptions(stack.workingLang), system, code, checkDisplay ? display : null); + txTime = txTime + (System.nanoTime() - t); + if (s == null) + return true; + if (s.isOk()) { + if (s.getMessage() != null) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + return true; + } + if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else if (s.getSeverity() == IssueSeverity.INFORMATION) + txHint(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else if (s.getSeverity() == IssueSeverity.WARNING) + txWarning(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, s.getMessage()); + else + return txRule(errors, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, I18nConstants.TERMINOLOGY_PASSTHROUGH_TX_MESSAGE, s.getMessage(), system, code); + return true; + } else if (system.startsWith("http://hl7.org/fhir")) { + if (Utilities.existsInList(system, "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/icd-10", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/srt")) + return true; // else don't check these (for now) + else if (system.startsWith("http://hl7.org/fhir/test")) + return true; // we don't validate these + else { + CodeSystem cs = getCodeSystem(system); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, I18nConstants.TERMINOLOGY_TX_SYSTEM_UNKNOWN, system)) { + ConceptDefinitionComponent def = getCodeDefinition(cs, code); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, I18nConstants.TERMINOLOGY_TX_CODE_UNKNOWN, system, code)) + return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), I18nConstants.TERMINOLOGY_TX_DISPLAY_WRONG, def.getDisplay()); } - } - - private void checkPrimitiveBinding(List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { - // We ignore bindings that aren't on string, uri or code - if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { - return; + return false; + } + } else if (context.isNoTerminologyServer() && Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://snomed.info/sct", "http://www.nlm.nih.gov/research/umls/rxnorm")) { + return true; // no checks in this case + } else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_INVALID, system); + return false; + } else { + try { + if (context.fetchResourceWithException(ValueSet.class, system) != null) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET, system); + // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. } - if (noTerminologyChecks) - return; - - String value = element.primitiveValue(); - // System.out.println("check "+value+" in "+path); - - // firstly, resolve the value set - ElementDefinitionBindingComponent binding = elementContext.getBinding(); - if (binding.hasValueSet()) { - ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, "ValueSet {0} not found by validator", describeReference(binding.getValueSet()))) { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), value, vs); - } - txTime = txTime + (System.nanoTime() - t); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') could not be validated in the absence of a terminology server"); - else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)" + getErrorMessage(vr.getMessage())); - 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) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)" + getErrorMessage(vr.getMessage())); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('" + value + "') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is recommended to come from this value set)" + getErrorMessage(vr.getMessage())); - } - } - } - } - } else if (!noBindingMsgSuppressed) - hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), "Binding has no source, so can't be checked"); + hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system); + return true; + } catch (Exception e) { + return true; + } } + } - private void checkQuantity(List 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); - checkFixedValue(errors, path + ".units", focus.getNamedChild("unit"), fixed.getUnitElement(), fixedSource, "units", focus, pattern); - checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); - checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); - } + private boolean startsWithButIsNot(String system, String... uri) { + for (String s : uri) + if (!system.equals(s) && system.startsWith(s)) + return true; + return false; + } - // implementation - private void checkRange(List errors, String path, Element focus, Range fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".low", focus.getNamedChild("low"), fixed.getLow(), fixedSource, "low", focus, pattern); - checkFixedValue(errors, path + ".high", focus.getNamedChild("high"), fixed.getHigh(), fixedSource, "high", focus, pattern); - - } - - private void checkRatio(List errors, String path, Element focus, Ratio fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".numerator", focus.getNamedChild("numerator"), fixed.getNumerator(), fixedSource, "numerator", focus, pattern); - checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), fixedSource, "denominator", focus, pattern); - } - - private void checkReference(ValidatorHostContext hostContext, List errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException { - Reference reference = ObjectConverter.readAsReference(element); - - String ref = reference.getReference(); - if (Utilities.noString(ref)) { - if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display"); - } - return; - } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { - // special known URLs that can't be validated but are known to be valid - return; + 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; + } - ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); - String refType; - if (ref.startsWith("#")) { - refType = "contained"; - } else { - if (we == null) { - refType = "remote"; + private void checkCodeableConcept(List errors, String path, Element focus, CodeableConcept fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + List codings = new ArrayList(); + focus.getNamedChildren("coding", codings); + if (pattern) { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() >= fixed.getCoding().size(), I18nConstants.TERMINOLOGY_TX_CODING_COUNT, Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + for (int i = 0; i < fixed.getCoding().size(); i++) { + Coding fixedCoding = fixed.getCoding().get(i); + boolean found = false; + List allErrorsFixed = new ArrayList<>(); + List errorsFixed; + for (int j = 0; j < codings.size() && !found; ++j) { + errorsFixed = new ArrayList<>(); + checkFixedValue(errorsFixed, path + ".coding", codings.get(j), fixedCoding, fixedSource, "coding", focus, pattern); + if (!hasErrors(errorsFixed)) { + found = true; } else { - refType = "bundled"; + errorsFixed + .stream() + .filter(t -> t.getLevel().ordinal() >= IssueSeverity.ERROR.ordinal()) + .forEach(t -> allErrorsFixed.add(t)); } + } + if (!found) { + // The argonaut DSTU2 labs profile requires userSelected=false on the category.coding and this + // needs to produce an understandable error message + String message = "Expected CodeableConcept " + (pattern ? "pattern" : "fixed value") + " not found for" + + " system: " + fixedCoding.getSystemElement().asStringValue() + + " code: " + fixedCoding.getCodeElement().asStringValue() + + " display: " + fixedCoding.getDisplayElement().asStringValue(); + if (fixedCoding.hasUserSelected()) { + message += " userSelected: " + fixedCoding.getUserSelected(); + } + message += " - Issues: " + allErrorsFixed; + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, false, message); + } } - ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundled") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.getAppContext(), path, ref); + } + } else { + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, codings.size() == fixed.getCoding().size(), I18nConstants.TERMINOLOGY_TX_CODING_COUNT, Integer.toString(fixed.getCoding().size()), Integer.toString(codings.size()))) { + for (int i = 0; i < codings.size(); i++) + checkFixedValue(errors, path + ".coding", codings.get(i), fixed.getCoding().get(i), fixedSource, "coding", focus); + } + } + } - if (pol.checkExists()) { - if (we == null) { - if (fetcher == null) { - if (!refType.equals("contained")) - throw new FHIRException("Resource resolution services not provided"); - } else { - Element ext = null; - if (fetchCache.containsKey(ref)) { - ext = fetchCache.get(ref); - } else { - try { - ext = fetcher.fetch(hostContext.getAppContext(), ref); - } catch (IOException e) { - throw new FHIRException(e); - } - if (ext != null) { - fetchCache.put(ref, ext); - } - } - we = ext == null ? null : makeExternalRef(ext, path); + private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) { + 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, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + try { + CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); + if (!cc.hasCoding()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(binding.getValueSet()), valueset.getUrl()); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + else + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet()), valueset.getUrl()); } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), "Unable to resolve resource '" + ref + "'"); - } + } else { + long t = System.nanoTime(); - String ft; - if (we != null) - ft = we.getType(); + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + 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"), cc, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } + } + } else { + if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + 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"), cc, stack); + if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } + } + } + } else if (vr.getMessage() != null) { + res = false; + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + res = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).noCheckValueSetMembership(), nextCoding, valueset); + if (vr.getSeverity() != null) { + if (vr.getSeverity() == IssueSeverity.INFORMATION) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else if (vr.getSeverity() == IssueSeverity.WARNING) { + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } + } + } + } + } + txTime = txTime + (System.nanoTime() - t); + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + } + } + return res; + } + + private boolean checkTerminologyCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { + 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, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + try { + CodeableConcept cc = convertToCodeableConcept(element, logical); + if (!cc.hasCoding()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); + else + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet()), valueset.getUrl()); + } + } else { + long t = System.nanoTime(); + + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + 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"), cc, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); + } + } + } else { + if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + 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"), cc, stack); + if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + } + } + } + } else if (vr.getMessage() != null) { + res = false; + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + res = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + String nextCode = nextCoding.getCode(); + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); + if (!vr.isOk()) { + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem); + } + } + } + } + txTime = txTime + (System.nanoTime() - t); + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getMessage()); + } + // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. + if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { + checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + } + } + return res; + } + + private void checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { + Coding c = convertToCoding(element, logical); + String code = c.getCode(); + String system = c.getSystem(); + String display = c.getDisplay(); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE); + + if (system != null && code != null && !noTerminologyChecks) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, system); + try { + if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) + if (theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + try { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4, describeReference(binding.getValueSet(), valueset)); + 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"), c, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(binding.getValueSet(), valueset)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(binding.getValueSet(), valueset)); + } + } + } else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + 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"), c, stack); + else + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : "")); + } + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + } + } + } catch (Exception e) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING2, e.getMessage(), e.toString()); + } + } + } + + private CodeableConcept convertToCodeableConcept(Element element, StructureDefinition logical) { + CodeableConcept res = new CodeableConcept(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.getCodingFirstRep().setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.getCodingFirstRep().setSystem(url); + } else { + res.getCodingFirstRep().setSystem("urn:oid:" + oid); + } + } else if ("Coding.version".equals(m)) { + res.getCodingFirstRep().setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.getCodingFirstRep().setDisplay(list.get(0).primitiveValue()); + } else if ("CodeableConcept.text".equals(m)) { + res.setText(list.get(0).primitiveValue()); + } else if ("CodeableConcept.coding".equals(m)) { + StructureDefinition c = context.fetchTypeDefinition(ed.getTypeFirstRep().getCode()); + for (Element e : list) { + res.addCoding(convertToCoding(e, c)); + } + } + } + } + } + } + return res; + } + + private Coding convertToCoding(Element element, StructureDefinition logical) { + Coding res = new Coding(); + for (ElementDefinition ed : logical.getSnapshot().getElement()) { + if (Utilities.charCount(ed.getPath(), '.') == 1) { + List maps = getMapping("http://hl7.org/fhir/terminology-pattern", logical, ed); + for (String m : maps) { + String name = tail(ed.getPath()); + List list = new ArrayList<>(); + element.getNamedChildren(name, list); + if (!list.isEmpty()) { + if ("Coding.code".equals(m)) { + res.setCode(list.get(0).primitiveValue()); + } else if ("Coding.system[fmt:OID]".equals(m)) { + String oid = list.get(0).primitiveValue(); + String url = context.oid2Uri(oid); + if (url != null) { + res.setSystem(url); + } else { + res.setSystem("urn:oid:" + oid); + } + } else if ("Coding.version".equals(m)) { + res.setVersion(list.get(0).primitiveValue()); + } else if ("Coding.display".equals(m)) { + res.setDisplay(list.get(0).primitiveValue()); + } + } + } + } + } + return res; + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl), valueset.getUrl(), ccSummary(cc)); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX, e.getMessage()); + } + } + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl), valueset.getUrl(), c.getSystem(), c.getCode()); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX, e.getMessage()); + } + } + } + + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { + // TODO Auto-generated method stub + ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + try { + long t = System.nanoTime(); + ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), value, valueset); + txTime = txTime + (System.nanoTime() - t); + if (!vr.isOk()) { + if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + else + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl), valueset.getUrl(), "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX, e.getMessage()); + } + } + } + + private String ccSummary(CodeableConcept cc) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (Coding c : cc.getCoding()) + b.append(c.getSystem() + "#" + c.getCode()); + return b.toString(); + } + + private void checkCoding(List errors, String path, Element focus, Coding fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".version", focus.getNamedChild("version"), fixed.getVersionElement(), fixedSource, "version", focus, pattern); + checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); + checkFixedValue(errors, path + ".display", focus.getNamedChild("display"), fixed.getDisplayElement(), fixedSource, "display", focus, pattern); + checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), fixedSource, "userSelected", focus, pattern); + } + + private void checkCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) { + String code = element.getNamedChildValue("code"); + String system = element.getNamedChildValue("system"); + String display = element.getNamedChildValue("display"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE); + + if (system != null && code != null && !noTerminologyChecks) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, system); + try { + if (checkCode(errors, element, path, code, system, display, checkDisplay, stack)) + if (theElementCntext != null && theElementCntext.hasBinding()) { + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { + if (binding.hasValueSet()) { + ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + try { + Coding c = ObjectConverter.readAsCoding(element); + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), c, valueset); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (binding.getStrength() == BindingStrength.REQUIRED) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4, describeReference(binding.getValueSet(), valueset)); + 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"), c, stack); + else if (!noExtensibleWarnings) + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(binding.getValueSet(), valueset)); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(binding.getValueSet(), valueset)); + } + } + } else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + 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"), c, stack); + else + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage())); + } + } + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); + } + } + } else if (binding.hasValueSet()) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + } + } + } catch (Exception e) { + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING2, e.getMessage(), e.toString()); + } + } + } + + private boolean isValueSet(String url) { + try { + ValueSet vs = context.fetchResourceWithException(ValueSet.class, url); + return vs != null; + } catch (Exception e) { + return false; + } + } + + private void checkContactPoint(List errors, String path, Element focus, ContactPoint fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + + } + + private StructureDefinition checkExtension(ValidatorHostContext hostContext, List errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { + String url = element.getNamedChildValue("url"); + boolean isModifier = element.getName().equals("modifierExtension"); + + long t = System.nanoTime(); + StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null; + sdTime = sdTime + (System.nanoTime() - t); + if (ex == null) { + if (xverManager == null) { + xverManager = new XVerExtensionManager(context); + } + if (xverManager.matchingUrl(url)) { + switch (xverManager.status(url)) { + case BadVersion: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, I18nConstants.EXTENSION_EXT_VERSION_INVALID, url, xverManager.getVersion(url)); + break; + case Unknown: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, I18nConstants.EXTENSION_EXT_VERSION_INVALIDID, url, xverManager.getElementId(url)); + break; + case Invalid: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, I18nConstants.EXTENSION_EXT_VERSION_NOCHANGE, url, xverManager.getElementId(url)); + break; + case Valid: + ex = xverManager.makeDefinition(url); + context.generateSnapshot(ex); + context.cacheResource(ex); + break; + default: + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", false, I18nConstants.EXTENSION_EXT_VERSION_INTERNAL, url); + break; + } + } else if (extensionUrl != null && !isAbsolute(url)) { + if (extensionUrl.equals(profile.getUrl())) { + rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), I18nConstants.EXTENSION_EXT_SUBEXTENSION_INVALID, url, profile.getUrl()); + } + } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN_NOTHERE, url)) { + hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN, url); + } + } + if (ex != null) { + trackUsage(ex, hostContext, element); + if (def.getIsModifier()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY); + } else { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN); + } + // two questions + // 1. can this extension be used here? + checkExtensionContext(errors, resource, container, ex, containerStack, hostContext); + + if (isModifier) + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_Y, url); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_N, url); + + // check the type of the extension: + Set allowedTypes = listExtensionTypes(ex); + String actualType = getExtensionType(element); + if (actualType == null) + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.isEmpty(), I18nConstants.EXTENSION_EXT_SIMPLE, url); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowedTypes.contains(actualType), I18nConstants.EXTENSION_EXT_TYPE, url, allowedTypes.toString(), 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, true, url); + + } + return ex; + } + + private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { + for (ElementDefinition ed : profile.getSnapshot().getElement()) { + if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { + return true; + } + } + return false; + } + + private String getExtensionType(Element element) { + for (Element e : element.getChildren()) { + if (e.getName().startsWith("value")) { + String tn = e.getName().substring(5); + String ltn = Utilities.uncapitalize(tn); + if (isPrimitiveType(ltn)) + return ltn; else - ft = tryParse(ref); + return tn; + } + } + return null; + } - if (reference.hasType()) { // R4 onwards... - // the type has to match the specified - String tu = isAbsolute(reference.getType()) ? reference.getType() : "http://hl7.org/fhir/StructureDefinition/" + reference.getType(); - TypeRefComponent containerType = container.getType("Reference"); - if (!containerType.hasTargetProfile(tu) && !containerType.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { - boolean matchingResource = false; - for (CanonicalType target : containerType.getTargetProfile()) { - StructureDefinition sd = resolveProfile(profile, target.asStringValue()); - if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) { - matchingResource = true; - break; - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, - "The type '" + reference.getType() + "' is not a valid Target for this element (must be one of " + container.getType("Reference").getTargetProfile() + ")"); + private Set listExtensionTypes(StructureDefinition ex) { + ElementDefinition vd = null; + for (ElementDefinition ed : ex.getSnapshot().getElement()) { + if (ed.getPath().startsWith("Extension.value")) { + vd = ed; + break; + } + } + Set res = new HashSet(); + if (vd != null && !"0".equals(vd.getMax())) { + for (TypeRefComponent tr : vd.getType()) { + res.add(tr.getWorkingCode()); + } + } + return res; + } - } - // the type has to match the actual - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), "The specified type '" + reference.getType() + "' does not match the found type '" + ft + "'"); - } + private boolean checkExtensionContext(List errors, Element resource, Element container, StructureDefinition definition, NodeStack stack, ValidatorHostContext hostContext) { + String extUrl = definition.getUrl(); + boolean ok = false; + CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder(); + List plist = new ArrayList<>(); + plist.add(stripIndexes(stack.getLiteralPath())); + for (String s : stack.getLogicalPaths()) { + String p = stripIndexes(s); + // all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition.... + if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) { + return true; + } + plist.add(p); - if (we != null && pol.checkType()) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, "Unable to determine type of target resource")) { - // we validate as much as we can. First, can we infer a type from the profile? - boolean ok = false; - TypeRefComponent type = getReferenceTypeRef(container.getType()); - if (type.hasTargetProfile() && !type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { - Set types = new HashSet<>(); - List profiles = new ArrayList<>(); - for (UriType u : type.getTargetProfile()) { - StructureDefinition sd = resolveProfile(profile, u.getValue()); - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, "Unable to resolve the profile reference '" + u.getValue() + "'")) { - types.add(sd.getType()); - if (ft.equals(sd.getType())) { - ok = true; - profiles.add(sd); - } - } - } - if (!pol.checkValid()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, "Unable to find matching profile for " + ref + " (by type) among choices: " + StringUtils.join("; ", type.getTargetProfile())); - } else { - Map> badProfiles = new HashMap>(); - Map> goodProfiles = new HashMap>(); - int goodCount = 0; - for (StructureDefinition pr : profiles) { - List profileErrors = new ArrayList(); - validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack()); - if (!hasErrors(profileErrors)) { - goodCount++; - goodProfiles.put(pr, profileErrors); - trackUsage(pr, hostContext, element); - } else { - badProfiles.put(pr, profileErrors); - } - } - if (goodCount == 1) { - if (showMessagesFromReferences) { - for (ValidationMessage vm : goodProfiles.values().iterator().next()) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - - } else if (goodProfiles.size() == 0) { - if (!isShowMessagesFromReferences()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); - for (StructureDefinition sd : badProfiles.keySet()) { - slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); - } - } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, "Unable to find matching profile for " + ref + " among choices: " + asList(type.getTargetProfile())); - for (List messages : badProfiles.values()) { - for (ValidationMessage vm : messages) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - } - } else { - if (!isShowMessagesFromReferences()) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); - for (StructureDefinition sd : badProfiles.keySet()) { - slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); - } - } else { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Found multiple matching profiles for " + ref + " among choices: " + asListByUrl(goodProfiles.keySet())); - for (List messages : goodProfiles.values()) { - for (ValidationMessage vm : messages) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } - } - } - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + types.toString() + ")"); - } - if (type.hasAggregation()) { - boolean modeOk = false; - for (Enumeration mode : type.getAggregation()) { - if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained")) - modeOk = true; - else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled")) - modeOk = true; - else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) - modeOk = true; - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference"); - } - } - } - if (we == null) { - TypeRefComponent type = getReferenceTypeRef(container.getType()); - boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); - rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, "Bundled or contained reference not found within the bundle/resource " + ref); - } - if (we == null && ft != null && assumeValidRestReferences) { - // if we == null, we inferred ft from the reference. if we are told to treat this as gospel - TypeRefComponent type = getReferenceTypeRef(container.getType()); - Set types = new HashSet<>(); - for (CanonicalType tp : type.getTargetProfile()) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue()); - if (sd != null) { - types.add(sd.getType()); - } - } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), "The type '" + ft + "' implied by the reference URL " + ref + " is not a valid Target for this element (must be one of " + types + ")"); - - } - if (pol == ReferenceValidationPolicy.CHECK_VALID) { - // todo.... - } } - private String asListByUrl(Collection list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (StructureDefinition sd : list) { - b.append(sd.getUrl()); + for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) { + if (ok) { + break; + } + if (ctxt.getType() == ExtensionContextType.ELEMENT) { + String en = ctxt.getExpression(); + contexts.append("e:" + en); + if ("Element".equals(en)) { + ok = true; + } else if (en.equals("Resource") && container.isResource()) { + ok = true; } - return b.toString(); - } - - private String asList(Collection list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (CanonicalType c : list) { - b.append(c.getValue()); - } - return b.toString(); - } - - private boolean areAllBaseProfiles(List profiles) { - for (StructureDefinition sd : profiles) { - if (!sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { - return false; + for (String p : plist) { + if (ok) { + break; + } + if (p.equals(en)) { + ok = true; + } else { + String pn = p; + String pt = ""; + if (p.contains(".")) { + pn = p.substring(0, p.indexOf(".")); + pt = p.substring(p.indexOf(".")); } + StructureDefinition sd = context.fetchTypeDefinition(pn); + while (sd != null) { + if ((sd.getType() + pt).equals(en)) { + ok = true; + break; + } + if (sd.getBaseDefinition() != null) { + sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); + } else { + sd = null; + } + } + } } + } else if (ctxt.getType() == ExtensionContextType.EXTENSION) { + contexts.append("x:" + ctxt.getExpression()); + NodeStack estack = stack.parent; + if (estack != null && estack.getElement().fhirType().equals("Extension")) { + String ext = estack.element.getNamedChildValue("url"); + if (ctxt.getExpression().equals(ext)) { + ok = true; + } + } + } else if (ctxt.getType() == ExtensionContextType.FHIRPATH) { + contexts.append("p:" + ctxt.getExpression()); + // The context is all elements that match the FHIRPath query found in the expression. + List res = fpe.evaluate(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(ctxt.getExpression())); + if (res.contains(container)) { + ok = true; + } + } else { + throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); + } + } + if (!ok) { + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, I18nConstants.EXTENSION_EXT_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString()); + return false; + } else { + if (definition.hasContextInvariant()) { + for (StringType s : definition.getContextInvariant()) { + if (!fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), container, fpe.parse(s.getValue()))) { + rule(errors, IssueType.STRUCTURE, container.line(), container.col(), stack.literalPath, false, I18nConstants.PROFILE_EXT_NOT_HERE, extUrl, s.getValue()); + return false; + } + } + } + return true; + } + } + + private List fixContexts(String extUrl, List list) { + List res = new ArrayList<>(); + for (StructureDefinitionContextComponent ctxt : list) { + res.add(ctxt.copy()); + } + if ("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type".equals(extUrl)) { + list.get(0).setExpression("ElementDefinition.type"); + } + if ("http://hl7.org/fhir/StructureDefinition/regex".equals(extUrl)) { + list.get(1).setExpression("ElementDefinition.type"); + } + return list; + } + + private String stripIndexes(String path) { + boolean skip = false; + StringBuilder b = new StringBuilder(); + for (char c : path.toCharArray()) { + if (skip) { + if (c == ']') { + skip = false; + } + } else if (c == '[') { + skip = true; + } else { + b.append(c); + } + } + return b.toString(); + } + + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent) { + checkFixedValue(errors, path, focus, fixed, fixedSource, propName, parent, false); + } + + @SuppressWarnings("rawtypes") + private void checkFixedValue(List errors, String path, Element focus, org.hl7.fhir.r5.model.Element fixed, String fixedSource, String propName, Element parent, boolean pattern) { + if ((fixed == null || fixed.isEmpty()) && focus == null) { + ; // this is all good + } else if ((fixed == null || fixed.isEmpty()) && focus != null) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, pattern, I18nConstants.PROFILE_VAL_NOTALLOWED, focus.getName(), (pattern ? "pattern" : "fixed value")); + } else if (fixed != null && !fixed.isEmpty() && focus == null) { + rule(errors, IssueType.VALUE, parent == null ? -1 : parent.line(), parent == null ? -1 : parent.col(), path, false, I18nConstants.PROFILE_VAL_MISSINGELEMENT, propName, fixedSource); + } else { + String value = focus.primitiveValue(); + if (fixed instanceof org.hl7.fhir.r5.model.BooleanType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.BooleanType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.IntegerType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.IntegerType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DecimalType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.DecimalType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.Base64BinaryType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.Base64BinaryType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.InstantType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.InstantType) fixed).getValue().toString(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.InstantType) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.CodeType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.CodeType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.CodeType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.Enumeration) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.Enumeration) fixed).asStringValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.StringType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.StringType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.StringType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.UriType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UriType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.UriType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DateType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateType) fixed).getValue().toString(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.DateType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.DateTimeType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue().toString(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.DateTimeType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.OidType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.OidType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.OidType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.UuidType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.UuidType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.UuidType) fixed).getValue()); + else if (fixed instanceof org.hl7.fhir.r5.model.IdType) + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, check(((org.hl7.fhir.r5.model.IdType) fixed).getValue(), value), I18nConstants._DT_FIXED_WRONG, value, ((org.hl7.fhir.r5.model.IdType) fixed).getValue()); + else if (fixed instanceof Quantity) + checkQuantity(errors, path, focus, (Quantity) fixed, fixedSource, pattern); + else if (fixed instanceof Address) + checkAddress(errors, path, focus, (Address) fixed, fixedSource, pattern); + else if (fixed instanceof ContactPoint) + checkContactPoint(errors, path, focus, (ContactPoint) fixed, fixedSource, pattern); + else if (fixed instanceof Attachment) + checkAttachment(errors, path, focus, (Attachment) fixed, fixedSource, pattern); + else if (fixed instanceof Identifier) + checkIdentifier(errors, path, focus, (Identifier) fixed, fixedSource, pattern); + else if (fixed instanceof Coding) + checkCoding(errors, path, focus, (Coding) fixed, fixedSource, pattern); + else if (fixed instanceof HumanName) + checkHumanName(errors, path, focus, (HumanName) fixed, fixedSource, pattern); + else if (fixed instanceof CodeableConcept) + checkCodeableConcept(errors, path, focus, (CodeableConcept) fixed, fixedSource, pattern); + else if (fixed instanceof Timing) + checkTiming(errors, path, focus, (Timing) fixed, fixedSource, pattern); + else if (fixed instanceof Period) + checkPeriod(errors, path, focus, (Period) fixed, fixedSource, pattern); + else if (fixed instanceof Range) + checkRange(errors, path, focus, (Range) fixed, fixedSource, pattern); + else if (fixed instanceof Ratio) + checkRatio(errors, path, focus, (Ratio) fixed, fixedSource, pattern); + else if (fixed instanceof SampledData) + checkSampledData(errors, path, focus, (SampledData) fixed, fixedSource, pattern); + + else + rule(errors, IssueType.EXCEPTION, focus.line(), focus.col(), path, false, I18nConstants.INTERNAL_INT_BAD_TYPE, fixed.getClass().getName()); + List extensions = new ArrayList(); + focus.getNamedChildren("extension", extensions); + if (fixed.getExtension().size() == 0) { + rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == 0, I18nConstants.EXTENSION_EXT_FIXED_BANNED); + } else if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, extensions.size() == fixed.getExtension().size(), I18nConstants.EXTENSION_EXT_COUNT_MISMATCH, Integer.toString(fixed.getExtension().size()), Integer.toString(extensions.size()))) { + for (Extension e : fixed.getExtension()) { + Element ex = getExtensionByUrl(extensions, e.getUrl()); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, ex != null, I18nConstants.EXTENSION_EXT_COUNT_NOTFOUND, e.getUrl())) { + checkFixedValue(errors, path, ex.getNamedChild("extension").getNamedChild("value"), e.getValue(), fixedSource, "extension.value", ex.getNamedChild("extension")); + } + } + } + } + } + + private void checkHumanName(List errors, String path, Element focus, HumanName fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".text", focus.getNamedChild("text"), fixed.getTextElement(), fixedSource, "text", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + + List parts = new ArrayList(); + focus.getNamedChildren("family", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() > 0 == fixed.hasFamily(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_FAMILY, (fixed.hasFamily() ? "1" : "0"), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".family", parts.get(i), fixed.getFamilyElement(), fixedSource, "family", focus, pattern); + } + focus.getNamedChildren("given", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getGiven().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_GIVEN, Integer.toString(fixed.getGiven().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".given", parts.get(i), fixed.getGiven().get(i), fixedSource, "given", focus, pattern); + } + focus.getNamedChildren("prefix", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getPrefix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_PREFIX, Integer.toString(fixed.getPrefix().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".prefix", parts.get(i), fixed.getPrefix().get(i), fixedSource, "prefix", focus, pattern); + } + focus.getNamedChildren("suffix", parts); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, parts.size() == fixed.getSuffix().size(), I18nConstants.FIXED_TYPE_CHECKS_DT_NAME_SUFFIX, Integer.toString(fixed.getSuffix().size()), Integer.toString(parts.size()))) { + for (int i = 0; i < parts.size(); i++) + checkFixedValue(errors, path + ".suffix", parts.get(i), fixed.getSuffix().get(i), fixedSource, "suffix", focus, pattern); + } + } + + private void checkIdentifier(List errors, String path, Element element, ElementDefinition context) { + String system = element.getNamedChildValue("system"); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM); + } + + private void checkIdentifier(List errors, String path, Element focus, Identifier fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".use", focus.getNamedChild("use"), fixed.getUseElement(), fixedSource, "use", focus, pattern); + checkFixedValue(errors, path + ".type", focus.getNamedChild("type"), fixed.getType(), fixedSource, "type", focus, pattern); + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), fixedSource, "value", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriod(), fixedSource, "period", focus, pattern); + checkFixedValue(errors, path + ".assigner", focus.getNamedChild("assigner"), fixed.getAssigner(), fixedSource, "assigner", focus, pattern); + } + + private void checkPeriod(List errors, String path, Element focus, Period fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".start", focus.getNamedChild("start"), fixed.getStartElement(), fixedSource, "start", focus, pattern); + checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), fixedSource, "end", focus, pattern); + } + + private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { + if (isBlank(e.primitiveValue())) { + if (e.primitiveValue() == null) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT); + else if (e.primitiveValue().length() == 0) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY); + else if (StringUtils.isWhitespace(e.primitiveValue())) + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS); + return; + } + String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX); + if (regex != null) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX, e.primitiveValue(), regex); + + if (type.equals("boolean")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_BOOLEAN_VALUE); + } + if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) { + String url = e.primitiveValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_OID); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_UUID); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) + // work around an old invalid example in a core package + || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_WS, url); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + + if (type.equals("oid")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_START)) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_VALID); + } + if (type.equals("uuid")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT); + try { + UUID.fromString(url.substring(8)); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_UUID_VAID, ex.getMessage()); + } + } + + // now, do we check the URI target? + if (fetcher != null) { + boolean found; + try { + found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url); + } catch (IOException e1) { + found = false; + } + rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url); + } + } + if (type.equals("id")) { + // work around an old issue with ElementDefinition.id + if (!context.getPath().equals("ElementDefinition.id") && !VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_ID_VALID, e.primitiveValue()); + } + } + if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY)) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_WS); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH)) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + } + } + } + if (type.equals("dateTime")) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue() + .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + try { + DateTimeType dt = new DateTimeType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, ex.getMessage()); + } + } + if (type.equals("time")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue() + .matches("([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_TIME_VALID); + try { + TimeType dt = new TimeType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_TIME_VALID, ex.getMessage()); + } + } + if (type.equals("date")) { + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + try { + DateType dt = new DateType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID, ex.getMessage()); + } + } + if (type.equals("base64Binary")) { + String encoded = e.primitiveValue(); + if (isNotBlank(encoded)) { + /* + * Technically this is not bulletproof as some invalid base64 won't be caught, + * but I think it's good enough. The original code used Java8 Base64 decoder + * but I've replaced it with a regex for 2 reasons: + * 1. This code will run on any version of Java + * 2. This code doesn't actually decode, which is much easier on memory use for big payloads + */ + int charCount = 0; + for (int i = 0; i < encoded.length(); i++) { + char nextChar = encoded.charAt(i); + if (Character.isWhitespace(nextChar)) { + continue; + } + if (Character.isLetterOrDigit(nextChar)) { + charCount++; + } + if (nextChar == '/' || nextChar == '=' || nextChar == '+') { + charCount++; + } + } + + if (charCount > 0 && charCount % 4 != 0) { + String value = encoded.length() < 100 ? encoded : "(snip)"; + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_BASE64_VALID, value); + } + } + } + if (type.equals("integer") || type.equals("unsignedInt") || type.equals("positiveInt")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isInteger(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_VALID, e.primitiveValue())) { + Integer v = new Integer(e.getValue()).intValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() || !context.getMaxValueIntegerType().hasValue() || (context.getMaxValueIntegerType().getValue() >= v), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_GT, (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() || !context.getMinValueIntegerType().hasValue() || (context.getMinValueIntegerType().getValue() <= v), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT, (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : "")); + if (type.equals("unsignedInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0); + if (type.equals("positiveInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1); + } + } + if (type.equals("integer64")) { + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isLong(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER64_VALID, e.primitiveValue())) { + Long v = new Long(e.getValue()).longValue(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueInteger64Type() || !context.getMaxValueInteger64Type().hasValue() || (context.getMaxValueInteger64Type().getValue() >= v), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_GT, (context.hasMaxValueInteger64Type() ? context.getMaxValueInteger64Type() : "")); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueInteger64Type() || !context.getMinValueInteger64Type().hasValue() || (context.getMinValueInteger64Type().getValue() <= v), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT, (context.hasMinValueInteger64Type() ? context.getMinValueInteger64Type() : "")); + if (type.equals("unsignedInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v >= 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0); + if (type.equals("positiveInt")) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, v > 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1); + } + } + if (type.equals("decimal")) { + if (e.primitiveValue() != null) { + DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false); + if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID, e.primitiveValue())) + warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE, e.primitiveValue()); + } + } + if (type.equals("instant")) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, + e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX, e.primitiveValue()); + warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue()); + try { + InstantType dt = new InstantType(e.primitiveValue()); + } catch (Exception ex) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID, ex.getMessage()); + } + } + + if (type.equals("code") && e.primitiveValue() != null) { + // Technically, a code is restricted to string which has at least one character and no leading or trailing whitespace, and where there is no whitespace + // other than single spaces in the contents + rule(errors, IssueType.INVALID, e.line(), e.col(), path, passesCodeWhitespaceRules(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CODE_WS, e.primitiveValue()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); + } + + if (context.hasBinding() && e.primitiveValue() != null) { + checkPrimitiveBinding(errors, path, type, context, e, profile, node); + } + + if (type.equals("xhtml")) { + XhtmlNode xhtml = e.getXhtml(); + if (xhtml != null) { // if it is null, this is an error already noted in the parsers + // check that the namespace is there and correct. + String ns = xhtml.getNsDecl(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.XHTML_NS.equals(ns), I18nConstants.XHTML_XHTML_NS_INVALID, ns, FormatUtilities.XHTML_NS); + // check that inner namespaces are all correct + checkInnerNS(errors, e, path, xhtml.getChildNodes()); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, "div".equals(xhtml.getName()), I18nConstants.XHTML_XHTML_NAME_INVALID, ns); + // check that no illegal elements and attributes have been used + checkInnerNames(errors, e, path, xhtml.getChildNodes()); + } + } + + if (context.hasFixed()) { + checkFixedValue(errors, path, e, context.getFixed(), profile.getUrl(), context.getSliceName(), null, false); + } + if (context.hasPattern()) { + checkFixedValue(errors, path, e, context.getPattern(), profile.getUrl(), context.getSliceName(), null, true); + } + + // for nothing to check + } + + private boolean isDefinitionURL(String url) { + return Utilities.existsInList(url, "http://hl7.org/fhirpath/System.Boolean", "http://hl7.org/fhirpath/System.String", "http://hl7.org/fhirpath/System.Integer", + "http://hl7.org/fhirpath/System.Decimal", "http://hl7.org/fhirpath/System.Date", "http://hl7.org/fhirpath/System.Time", "http://hl7.org/fhirpath/System.DateTime", "http://hl7.org/fhirpath/System.Quantity"); + } + + private void checkInnerNames(List errors, Element e, String path, List list) { + for (XhtmlNode node : list) { + if (node.getNodeType() == NodeType.Element) { + rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.existsInList(node.getName(), + "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", + "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", + "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", + "code", "samp", "img", "map", "area" + + ), I18nConstants.XHTML_XHTML_ELEMENT_ILLEGAL, node.getName()); + for (String an : node.getAttributes().keySet()) { + boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, + "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", + // tables + "span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") || + + Utilities.existsInList(node.getName() + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite", + "a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src", + "img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape", + "area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border", + "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" + ); + if (!ok) + rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.XHTML_XHTML_ATTRIBUTE_ILLEGAL, an, node.getName()); + } + checkInnerNames(errors, e, path, node.getChildNodes()); + } + } + } + + private void checkInnerNS(List errors, Element e, String path, List list) { + for (XhtmlNode node : list) { + if (node.getNodeType() == NodeType.Element) { + String ns = node.getNsDecl(); + rule(errors, IssueType.INVALID, e.line(), e.col(), path, ns == null || FormatUtilities.XHTML_NS.equals(ns), I18nConstants.XHTML_XHTML_NS_INVALID, ns, FormatUtilities.XHTML_NS); + checkInnerNS(errors, e, path, node.getChildNodes()); + } + } + } + + private void checkPrimitiveBinding(List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { + // We ignore bindings that aren't on string, uri or code + if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { + return; + } + if (noTerminologyChecks) + return; + + String value = element.primitiveValue(); + // System.out.println("check "+value+" in "+path); + + // firstly, resolve the value set + ElementDefinitionBindingComponent binding = elementContext.getBinding(); + if (binding.hasValueSet()) { + ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); + if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND2, describeReference(binding.getValueSet()))) { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + vr = context.validateCode(new ValidationOptions(stack.workingLang), value, vs); + } + txTime = txTime + (System.nanoTime() - t); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_15, value); + else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + 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) + txWarning(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) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + } + } + } + } + } else if (!noBindingMsgSuppressed) + hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !type.equals("code"), I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE2); + } + + private void checkQuantity(List 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); + checkFixedValue(errors, path + ".units", focus.getNamedChild("unit"), fixed.getUnitElement(), fixedSource, "units", focus, pattern); + checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), fixedSource, "system", focus, pattern); + checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern); + } + + // implementation + + private void checkRange(List errors, String path, Element focus, Range fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".low", focus.getNamedChild("low"), fixed.getLow(), fixedSource, "low", focus, pattern); + checkFixedValue(errors, path + ".high", focus.getNamedChild("high"), fixed.getHigh(), fixedSource, "high", focus, pattern); + + } + + private void checkRatio(List errors, String path, Element focus, Ratio fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".numerator", focus.getNamedChild("numerator"), fixed.getNumerator(), fixedSource, "numerator", focus, pattern); + checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), fixedSource, "denominator", focus, pattern); + } + + private void checkReference(ValidatorHostContext hostContext, List errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException { + Reference reference = ObjectConverter.readAsReference(element); + + String ref = reference.getReference(); + if (Utilities.noString(ref)) { + if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), I18nConstants.REFERENCE_REF_NODISPLAY); + } + return; + } else if (Utilities.existsInList(ref, "http://tools.ietf.org/html/bcp47")) { + // special known URLs that can't be validated but are known to be valid + return; + } + + ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); + String refType; + if (ref.startsWith("#")) { + refType = "contained"; + } else { + if (we == null) { + refType = "remote"; + } else { + refType = "bundled"; + } + } + ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundled") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.getAppContext(), path, ref); + + if (pol.checkExists()) { + if (we == null) { + if (fetcher == null) { + if (!refType.equals("contained")) + throw new FHIRException("Resource resolution services not provided"); + } else { + Element ext = null; + if (fetchCache.containsKey(ref)) { + ext = fetchCache.get(ref); + } else { + try { + ext = fetcher.fetch(hostContext.getAppContext(), ref); + } catch (IOException e) { + throw new FHIRException(e); + } + if (ext != null) { + fetchCache.put(ref, ext); + } + } + we = ext == null ? null : makeExternalRef(ext, path); + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), I18nConstants.REFERENCE_REF_CANTRESOLVE, ref); + } + + String ft; + if (we != null) + ft = we.getType(); + else + ft = tryParse(ref); + + if (reference.hasType()) { // R4 onwards... + // the type has to match the specified + String tu = isAbsolute(reference.getType()) ? reference.getType() : "http://hl7.org/fhir/StructureDefinition/" + reference.getType(); + TypeRefComponent containerType = container.getType("Reference"); + if (!containerType.hasTargetProfile(tu) && !containerType.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { + boolean matchingResource = false; + for (CanonicalType target : containerType.getTargetProfile()) { + StructureDefinition sd = resolveProfile(profile, target.asStringValue()); + if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) { + matchingResource = true; + break; + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, I18nConstants.REFERENCE_REF_WRONGTARGET, reference.getType(), container.getType("Reference").getTargetProfile()); + + } + // the type has to match the actual + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), I18nConstants.REFERENCE_REF_BADTARGETTYPE, reference.getType(), ft); + } + + if (we != null && pol.checkType()) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, I18nConstants.REFERENCE_REF_NOTYPE)) { + // we validate as much as we can. First, can we infer a type from the profile? + boolean ok = false; + TypeRefComponent type = getReferenceTypeRef(container.getType()); + if (type.hasTargetProfile() && !type.hasTargetProfile("http://hl7.org/fhir/StructureDefinition/Resource")) { + Set types = new HashSet<>(); + List profiles = new ArrayList<>(); + for (UriType u : type.getTargetProfile()) { + StructureDefinition sd = resolveProfile(profile, u.getValue()); + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, u.getValue())) { + types.add(sd.getType()); + if (ft.equals(sd.getType())) { + ok = true; + profiles.add(sd); + } + } + } + if (!pol.checkValid()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, I18nConstants.REFERENCE_REF_CANTMATCHTYPE, ref, StringUtils.join("; ", type.getTargetProfile())); + } else { + Map> badProfiles = new HashMap>(); + Map> goodProfiles = new HashMap>(); + int goodCount = 0; + for (StructureDefinition pr : profiles) { + List profileErrors = new ArrayList(); + validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack()); + if (!hasErrors(profileErrors)) { + goodCount++; + goodProfiles.put(pr, profileErrors); + trackUsage(pr, hostContext, element); + } else { + badProfiles.put(pr, profileErrors); + } + } + if (goodCount == 1) { + if (showMessagesFromReferences) { + for (ValidationMessage vm : goodProfiles.values().iterator().next()) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + + } else if (goodProfiles.size() == 0) { + if (!isShowMessagesFromReferences()) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); + for (StructureDefinition sd : badProfiles.keySet()) { + slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); + } + } else { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); + for (List messages : badProfiles.values()) { + for (ValidationMessage vm : messages) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + } + } else { + if (!isShowMessagesFromReferences()) { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); + for (StructureDefinition sd : badProfiles.keySet()) { + slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + ref + " matching against Profile" + sd.getUrl(), errorSummaryForSlicingAsHtml(badProfiles.get(sd))); + } + } else { + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); + for (List messages : goodProfiles.values()) { + for (ValidationMessage vm : messages) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + } + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE, ft, types.toString()); + } + if (type.hasAggregation()) { + boolean modeOk = false; + for (Enumeration mode : type.getAggregation()) { + if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained")) + modeOk = true; + else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled")) + modeOk = true; + else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) + modeOk = true; + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, I18nConstants.REFERENCE_REF_AGGREGATION, refType); + } + } + } + if (we == null) { + TypeRefComponent type = getReferenceTypeRef(container.getType()); + boolean okToRef = !type.hasAggregation() || type.hasAggregation(AggregationMode.REFERENCED); + rule(errors, IssueType.REQUIRED, -1, -1, path, okToRef, I18nConstants.REFERENCE_REF_NOTFOUND_BUNDLE, ref); + } + if (we == null && ft != null && assumeValidRestReferences) { + // if we == null, we inferred ft from the reference. if we are told to treat this as gospel + TypeRefComponent type = getReferenceTypeRef(container.getType()); + Set types = new HashSet<>(); + for (CanonicalType tp : type.getTargetProfile()) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, tp.getValue()); + if (sd != null) { + types.add(sd.getType()); + } + } + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || types.contains(ft), I18nConstants.REFERENCE_REF_BADTARGETTYPE2, ft, ref, types); + + } + if (pol == ReferenceValidationPolicy.CHECK_VALID) { + // todo.... + } + } + + private String asListByUrl(Collection list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (StructureDefinition sd : list) { + b.append(sd.getUrl()); + } + return b.toString(); + } + + private String asList(Collection list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (CanonicalType c : list) { + b.append(c.getValue()); + } + return b.toString(); + } + + private boolean areAllBaseProfiles(List profiles) { + for (StructureDefinition sd : profiles) { + if (!sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) { + return false; + } + } + return true; + } + + private String errorSummaryForSlicing(List list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ValidationMessage vm : list) { + if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL || vm.isSlicingHint()) { + b.append(vm.getLocation() + ": " + vm.getMessage()); + } + } + return b.toString(); + } + + private String errorSummaryForSlicingAsHtml(List list) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (ValidationMessage vm : list) { + if (vm.isSlicingHint()) { + b.append("

  • " + vm.getLocation() + ": " + vm.getSliceHtml() + "
  • "); + } else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) { + b.append("
  • " + vm.getLocation() + ": " + vm.getHtml() + "
  • "); + } + } + return "
      " + b.toString() + "
    "; + } + + private TypeRefComponent getReferenceTypeRef(List types) { + for (TypeRefComponent tr : types) { + if ("Reference".equals(tr.getCode())) { + return tr; + } + } + return null; + } + + private String checkResourceType(String type) { + long t = System.nanoTime(); + try { + if (context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + type) != null) + return type; + else + return null; + } finally { + sdTime = sdTime + (System.nanoTime() - t); + } + } + + private void checkSampledData(List errors, String path, Element focus, SampledData fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".origin", focus.getNamedChild("origin"), fixed.getOrigin(), fixedSource, "origin", focus, pattern); + checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriodElement(), fixedSource, "period", focus, pattern); + checkFixedValue(errors, path + ".factor", focus.getNamedChild("factor"), fixed.getFactorElement(), fixedSource, "factor", focus, pattern); + checkFixedValue(errors, path + ".lowerLimit", focus.getNamedChild("lowerLimit"), fixed.getLowerLimitElement(), fixedSource, "lowerLimit", focus, pattern); + checkFixedValue(errors, path + ".upperLimit", focus.getNamedChild("upperLimit"), fixed.getUpperLimitElement(), fixedSource, "upperLimit", focus, pattern); + checkFixedValue(errors, path + ".dimensions", focus.getNamedChild("dimensions"), fixed.getDimensionsElement(), fixedSource, "dimensions", focus, pattern); + checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); + } + + private void checkTiming(List errors, String path, Element focus, Timing fixed, String fixedSource, boolean pattern) { + checkFixedValue(errors, path + ".repeat", focus.getNamedChild("repeat"), fixed.getRepeat(), fixedSource, "value", focus, pattern); + + List events = new ArrayList(); + focus.getNamedChildren("event", events); + if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(), I18nConstants.BUNDLE_MSG_EVENT_COUNT, Integer.toString(fixed.getEvent().size()), Integer.toString(events.size()))) { + for (int i = 0; i < events.size(); i++) + checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); + } + } + + private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) { + for (ValueSetExpansionContainsComponent c : cnt.getContains()) { + if (code.equals(c.getCode()) && system.equals(c.getSystem().toString())) + return true; + if (codeinExpansion(c, system, code)) return true; } + return false; + } - private String errorSummaryForSlicing(List list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (ValidationMessage vm : list) { - if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL || vm.isSlicingHint()) { - b.append(vm.getLocation() + ": " + vm.getMessage()); - } + private boolean codeInExpansion(ValueSet vs, String system, String code) { + for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { + if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem()))) + return true; + if (codeinExpansion(c, system, code)) + return true; + } + return false; + } + + private String describeReference(String reference) { + if (reference == null) + return "null"; + return reference; + } + + private String describeReference(String reference, CanonicalResource target) { + if (reference == null && target == null) + return "null"; + if (reference == null) { + return target.getUrl(); + } + if (target == null) { + return reference; + } + if (reference.equals(target.getUrl())) { + return reference; + } + return reference + "(which actually refers to " + target.getUrl() + ")"; + } + + private String describeTypes(List types) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (TypeRefComponent t : types) { + b.append(t.getWorkingCode()); + } + return b.toString(); + } + + protected ElementDefinition findElement(StructureDefinition profile, String name) { + for (ElementDefinition c : profile.getSnapshot().getElement()) { + if (c.getPath().equals(name)) { + return c; + } + } + return null; + } + + public BestPracticeWarningLevel getBestPracticeWarningLevel() { + return bpWarnings; + } + + @Override + public CheckDisplayOption getCheckDisplay() { + return checkDisplay; + } + + private ConceptDefinitionComponent getCodeDefinition(ConceptDefinitionComponent c, String code) { + if (code.equals(c.getCode())) + return c; + for (ConceptDefinitionComponent g : c.getConcept()) { + ConceptDefinitionComponent r = getCodeDefinition(g, code); + if (r != null) + return r; + } + return null; + } + + private ConceptDefinitionComponent getCodeDefinition(CodeSystem cs, String code) { + for (ConceptDefinitionComponent c : cs.getConcept()) { + ConceptDefinitionComponent r = getCodeDefinition(c, code); + if (r != null) + return r; + } + return null; + } + + private IndexedElement getContainedById(Element container, String id) { + List contained = new ArrayList(); + container.getNamedChildren("contained", contained); + for (int i = 0; i < contained.size(); i++) { + Element we = contained.get(i); + if (id.equals(we.getNamedChildValue("id"))) { + return new IndexedElement(i, we, null); + } + } + return null; + } + + public IWorkerContext getContext() { + return context; + } + + private List getCriteriaForDiscriminator(String path, ElementDefinition element, String discriminator, StructureDefinition profile, boolean removeResolve) throws FHIRException { + List elements = new ArrayList(); + if ("value".equals(discriminator) && element.hasFixed()) { + elements.add(element); + return elements; + } + + if (removeResolve) { // if we're doing profile slicing, we don't want to walk into the last resolve.. we need the profile on the source not the target + if (discriminator.equals("resolve()")) { + elements.add(element); + return elements; + } + if (discriminator.endsWith(".resolve()")) + discriminator = discriminator.substring(0, discriminator.length() - 10); + } + + ElementDefinition ed = null; + ExpressionNode expr = fpe.parse(fixExpr(discriminator)); + long t2 = System.nanoTime(); + ed = fpe.evaluateDefinition(expr, profile, element); + sdTime = sdTime + (System.nanoTime() - t2); + if (ed != null) + elements.add(ed); + + for (TypeRefComponent type : element.getType()) { + for (CanonicalType p : type.getProfile()) { + String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; + StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); + if (sd == null) + throw new DefinitionException("Unable to resolve profile " + p); + profile = sd; + if (id == null) + element = sd.getSnapshot().getElementFirstRep(); + else { + element = null; + for (ElementDefinition t : sd.getSnapshot().getElement()) { + if (id.equals(t.getId())) + element = t; + } + if (element == null) + throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); } - return b.toString(); - } - - private String errorSummaryForSlicingAsHtml(List list) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (ValidationMessage vm : list) { - if (vm.isSlicingHint()) { - b.append("
  • " + vm.getLocation() + ": " + vm.getSliceHtml() + "
  • "); - } else if (vm.getLevel() == IssueSeverity.ERROR || vm.getLevel() == IssueSeverity.FATAL) { - b.append("
  • " + vm.getLocation() + ": " + vm.getHtml() + "
  • "); - } - } - return "
      " + b.toString() + "
    "; - } - - private TypeRefComponent getReferenceTypeRef(List types) { - for (TypeRefComponent tr : types) { - if ("Reference".equals(tr.getCode())) { - return tr; - } - } - return null; - } - - private String checkResourceType(String type) { - long t = System.nanoTime(); - try { - if (context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + type) != null) - return type; - else - return null; - } finally { - sdTime = sdTime + (System.nanoTime() - t); - } - } - - private void checkSampledData(List errors, String path, Element focus, SampledData fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".origin", focus.getNamedChild("origin"), fixed.getOrigin(), fixedSource, "origin", focus, pattern); - checkFixedValue(errors, path + ".period", focus.getNamedChild("period"), fixed.getPeriodElement(), fixedSource, "period", focus, pattern); - checkFixedValue(errors, path + ".factor", focus.getNamedChild("factor"), fixed.getFactorElement(), fixedSource, "factor", focus, pattern); - checkFixedValue(errors, path + ".lowerLimit", focus.getNamedChild("lowerLimit"), fixed.getLowerLimitElement(), fixedSource, "lowerLimit", focus, pattern); - checkFixedValue(errors, path + ".upperLimit", focus.getNamedChild("upperLimit"), fixed.getUpperLimitElement(), fixedSource, "upperLimit", focus, pattern); - checkFixedValue(errors, path + ".dimensions", focus.getNamedChild("dimensions"), fixed.getDimensionsElement(), fixedSource, "dimensions", focus, pattern); - checkFixedValue(errors, path + ".data", focus.getNamedChild("data"), fixed.getDataElement(), fixedSource, "data", focus, pattern); - } - - private void checkTiming(List errors, String path, Element focus, Timing fixed, String fixedSource, boolean pattern) { - checkFixedValue(errors, path + ".repeat", focus.getNamedChild("repeat"), fixed.getRepeat(), fixedSource, "value", focus, pattern); - - List events = new ArrayList(); - focus.getNamedChildren("event", events); - if (rule(errors, IssueType.VALUE, focus.line(), focus.col(), path, events.size() == fixed.getEvent().size(), - "Expected " + Integer.toString(fixed.getEvent().size()) + " but found " + Integer.toString(events.size()) + " event elements")) { - for (int i = 0; i < events.size(); i++) - checkFixedValue(errors, path + ".event", events.get(i), fixed.getEvent().get(i), fixedSource, "event", focus, pattern); - } - } - - private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) { - for (ValueSetExpansionContainsComponent c : cnt.getContains()) { - if (code.equals(c.getCode()) && system.equals(c.getSystem().toString())) - return true; - if (codeinExpansion(c, system, code)) - return true; - } - return false; - } - - private boolean codeInExpansion(ValueSet vs, String system, String code) { - for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { - if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem()))) - return true; - if (codeinExpansion(c, system, code)) - return true; - } - return false; - } - - private String describeReference(String reference) { - if (reference == null) - return "null"; - return reference; - } - - private String describeReference(String reference, CanonicalResource target) { - if (reference == null && target == null) - return "null"; - if (reference == null) { - return target.getUrl(); - } - if (target == null) { - return reference; - } - if (reference.equals(target.getUrl())) { - return reference; - } - return reference + "(which actually refers to " + target.getUrl() + ")"; - } - - private String describeTypes(List types) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (TypeRefComponent t : types) { - b.append(t.getWorkingCode()); - } - return b.toString(); - } - - protected ElementDefinition findElement(StructureDefinition profile, String name) { - for (ElementDefinition c : profile.getSnapshot().getElement()) { - if (c.getPath().equals(name)) { - return c; - } - } - return null; - } - - public BestPracticeWarningLevel getBestPracticeWarningLevel() { - return bpWarnings; - } - - @Override - public CheckDisplayOption getCheckDisplay() { - return checkDisplay; - } - - private ConceptDefinitionComponent getCodeDefinition(ConceptDefinitionComponent c, String code) { - if (code.equals(c.getCode())) - return c; - for (ConceptDefinitionComponent g : c.getConcept()) { - ConceptDefinitionComponent r = getCodeDefinition(g, code); - if (r != null) - return r; - } - return null; - } - - private ConceptDefinitionComponent getCodeDefinition(CodeSystem cs, String code) { - for (ConceptDefinitionComponent c : cs.getConcept()) { - ConceptDefinitionComponent r = getCodeDefinition(c, code); - if (r != null) - return r; - } - return null; - } - - private IndexedElement getContainedById(Element container, String id) { - List contained = new ArrayList(); - container.getNamedChildren("contained", contained); - for (int i = 0; i < contained.size(); i++) { - Element we = contained.get(i); - if (id.equals(we.getNamedChildValue("id"))) { - return new IndexedElement(i, we, null); - } - } - return null; - } - - public IWorkerContext getContext() { - return context; - } - - private List getCriteriaForDiscriminator(String path, ElementDefinition element, String discriminator, StructureDefinition profile, boolean removeResolve) throws FHIRException { - List elements = new ArrayList(); - if ("value".equals(discriminator) && element.hasFixed()) { - elements.add(element); - return elements; - } - - if (removeResolve) { // if we're doing profile slicing, we don't want to walk into the last resolve.. we need the profile on the source not the target - if (discriminator.equals("resolve()")) { - elements.add(element); - return elements; - } - if (discriminator.endsWith(".resolve()")) - discriminator = discriminator.substring(0, discriminator.length() - 10); - } - - ElementDefinition ed = null; - ExpressionNode expr = fpe.parse(fixExpr(discriminator)); - long t2 = System.nanoTime(); + expr = fpe.parse(fixExpr(discriminator)); + t2 = System.nanoTime(); ed = fpe.evaluateDefinition(expr, profile, element); sdTime = sdTime + (System.nanoTime() - t2); if (ed != null) - elements.add(ed); + elements.add(ed); + } + } + return elements; + } - for (TypeRefComponent type : element.getType()) { - for (CanonicalType p : type.getProfile()) { - String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; - StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); - if (sd == null) - throw new DefinitionException("Unable to resolve profile " + p); - profile = sd; - if (id == null) - element = sd.getSnapshot().getElementFirstRep(); - else { - element = null; - for (ElementDefinition t : sd.getSnapshot().getElement()) { - if (id.equals(t.getId())) - element = t; - } - if (element == null) - throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); - } - expr = fpe.parse(fixExpr(discriminator)); - t2 = System.nanoTime(); - ed = fpe.evaluateDefinition(expr, profile, element); - sdTime = sdTime + (System.nanoTime() - t2); - if (ed != null) - elements.add(ed); - } + + private Element getExtensionByUrl(List extensions, String urlSimple) { + for (Element e : extensions) { + if (urlSimple.equals(e.getNamedChildValue("url"))) + return e; + } + return null; + } + + public List getExtensionDomains() { + return extensionDomains; + } + + private IndexedElement getFromBundle(Element bundle, String ref, String fullUrl, List errors, String path, String type, boolean isTransaction) { + String targetUrl = null; + String version = ""; + String resourceType = null; + if (ref.startsWith("http") || ref.startsWith("urn")) { + // We've got an absolute reference, no need to calculate + if (ref.contains("/_history/")) { + targetUrl = ref.substring(0, ref.indexOf("/_history/") - 1); + version = ref.substring(ref.indexOf("/_history/") + 10); + } else + targetUrl = ref; + + } else if (fullUrl == null) { + //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error + // but this rule doesn't apply for batches or transactions + rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"), I18nConstants.BUNDLE_BUNDLE_FULLURL_MISSING); + return null; + + } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { + if (isTransaction) { + rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), I18nConstants.REFERENCE_REF_FORMAT1, ref); + } else { + rule(errors, IssueType.INVALID, -1, -1, path, false, I18nConstants.REFERENCE_REF_FORMAT2, ref); + } + return null; + + } else { + String base = ""; + if (fullUrl.startsWith("urn")) { + String[] parts = fullUrl.split("\\:"); + for (int i = 0; i < parts.length - 1; i++) { + base = base + parts[i] + ":"; } - return elements; - } - - - private Element getExtensionByUrl(List extensions, String urlSimple) { - for (Element e : extensions) { - if (urlSimple.equals(e.getNamedChildValue("url"))) - return e; + } else { + String[] parts; + parts = fullUrl.split("/"); + for (int i = 0; i < parts.length - 2; i++) { + base = base + parts[i] + "/"; } - return null; + } + + String id = null; + if (ref.contains("/_history/")) { + version = ref.substring(ref.indexOf("/_history/") + 10); + String[] refBaseParts = ref.substring(0, ref.indexOf("/_history/")).split("/"); + resourceType = refBaseParts[0]; + id = refBaseParts[1]; + } else if (base.startsWith("urn")) { + resourceType = ref.split("/")[0]; + id = ref.split("/")[1]; + } else + id = ref; + + targetUrl = base + id; } - public List getExtensionDomains() { - return extensionDomains; - } - - private IndexedElement getFromBundle(Element bundle, String ref, String fullUrl, List errors, String path, String type, boolean isTransaction) { - String targetUrl = null; - String version = ""; - String resourceType = null; - if (ref.startsWith("http") || ref.startsWith("urn")) { - // We've got an absolute reference, no need to calculate - if (ref.contains("/_history/")) { - targetUrl = ref.substring(0, ref.indexOf("/_history/") - 1); - version = ref.substring(ref.indexOf("/_history/") + 10); - } else - targetUrl = ref; - - } else if (fullUrl == null) { - //This isn't a problem for signatures - if it's a signature, we won't have a resolution for a relative reference. For anything else, this is an error - // but this rule doesn't apply for batches or transactions - rule(errors, IssueType.REQUIRED, -1, -1, path, Utilities.existsInList(type, "batch-response", "transaction-response") || path.startsWith("Bundle.signature"), "Relative Reference appears inside Bundle whose entry is missing a fullUrl"); - return null; - - } else if (ref.split("/").length != 2 && ref.split("/").length != 4) { - if (isTransaction) { - rule(errors, IssueType.INVALID, -1, -1, path, isSearchUrl(ref), "Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered " + ref); - } else { - rule(errors, IssueType.INVALID, -1, -1, path, false, "Relative URLs must be of the format [ResourceName]/[id]. Encountered " + ref); - } - return null; - + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + Element match = null; + int matchIndex = -1; + for (int i = 0; i < entries.size(); i++) { + Element we = entries.get(i); + if (targetUrl.equals(we.getChildValue("fullUrl"))) { + Element r = we.getNamedChild("resource"); + if (version.isEmpty()) { + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, I18nConstants.BUNDLE_BUNDLE_MULTIPLEMATCHES, ref); + match = r; + matchIndex = i; } else { - String base = ""; - if (fullUrl.startsWith("urn")) { - String[] parts = fullUrl.split("\\:"); - for (int i = 0; i < parts.length - 1; i++) { - base = base + parts[i] + ":"; - } - } else { - String[] parts; - parts = fullUrl.split("/"); - for (int i = 0; i < parts.length - 2; i++) { - base = base + parts[i] + "/"; - } + try { + if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { + rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, I18nConstants.BUNDLE_BUNDLE_MULTIPLEMATCHES, ref); + match = r; + matchIndex = i; } - - String id = null; - if (ref.contains("/_history/")) { - version = ref.substring(ref.indexOf("/_history/") + 10); - String[] refBaseParts = ref.substring(0, ref.indexOf("/_history/")).split("/"); - resourceType = refBaseParts[0]; - id = refBaseParts[1]; - } else if (base.startsWith("urn")) { - resourceType = ref.split("/")[0]; - id = ref.split("/")[1]; - } else - id = ref; - - targetUrl = base + id; + } catch (Exception e) { + warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null, I18nConstants.BUNDLE_BUNDLE_FULLURL_NEEDVERSION, targetUrl); + // If one of these things is null + } } - - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); - Element match = null; - int matchIndex = -1; - for (int i = 0; i < entries.size(); i++) { - Element we = entries.get(i); - if (targetUrl.equals(we.getChildValue("fullUrl"))) { - Element r = we.getNamedChild("resource"); - if (version.isEmpty()) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); - match = r; - matchIndex = i; - } else { - try { - if (version.equals(r.getChildren("meta").get(0).getChildValue("versionId"))) { - rule(errors, IssueType.FORBIDDEN, -1, -1, path, match == null, "Multiple matches in bundle for reference " + ref); - match = r; - matchIndex = i; - } - } catch (Exception e) { - warning(errors, IssueType.REQUIRED, -1, -1, path, r.getChildren("meta").size() == 1 && r.getChildren("meta").get(0).getChildValue("versionId") != null, "Entries matching fullURL " + targetUrl + " should declare meta/versionId because there are version-specific references"); - // If one of these things is null - } - } - } - } - - if (match != null && resourceType != null) - rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType), "Matching reference for reference " + ref + " has resourceType " + match.getType()); - if (match == null) - warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"), "URN reference is not locally contained within the bundle " + ref); - return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); + } } - private boolean isSearchUrl(String ref) { - if (Utilities.noString(ref) || !ref.contains("?")) { - return false; - } - String tn = ref.substring(0, ref.indexOf("?")); - String q = ref.substring(ref.indexOf("?") + 1); - if (!context.getResourceNames().contains(tn)) { - return false; - } else { - return q.matches("([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+)(&([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+))*"); - } - } + if (match != null && resourceType != null) + rule(errors, IssueType.REQUIRED, -1, -1, path, match.getType().equals(resourceType), I18nConstants.REFERENCE_REF_RESOURCETYPE, ref, match.getType()); + if (match == null) + warning(errors, IssueType.REQUIRED, -1, -1, path, !ref.startsWith("urn"), I18nConstants.BUNDLE_BUNDLE_NOT_LOCAL, ref); + return match == null ? null : new IndexedElement(matchIndex, match, entries.get(matchIndex)); + } - private StructureDefinition getProfileForType(String type, List list) { - for (TypeRefComponent tr : list) { - String url = tr.getWorkingCode(); - if (!Utilities.isAbsoluteUrl(url)) - url = "http://hl7.org/fhir/StructureDefinition/" + url; - long t = System.nanoTime(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - sdTime = sdTime + (System.nanoTime() - t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) - return sd; - } - return null; + private boolean isSearchUrl(String ref) { + if (Utilities.noString(ref) || !ref.contains("?")) { + return false; } + String tn = ref.substring(0, ref.indexOf("?")); + String q = ref.substring(ref.indexOf("?") + 1); + if (!context.getResourceNames().contains(tn)) { + return false; + } else { + return q.matches("([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+)(&([_a-zA-Z][_a-zA-Z0-9]*=[^=&]+))*"); + } + } - private Element getValueForDiscriminator(Object appContext, List errors, Element element, String discriminator, ElementDefinition criteria, NodeStack stack) throws FHIRException, IOException { - String p = stack.getLiteralPath() + "." + element.getName(); - Element focus = element; - String[] dlist = discriminator.split("\\."); - for (String d : dlist) { - if (focus.fhirType().equals("Reference") && d.equals("reference")) { - String url = focus.getChildValue("reference"); - if (Utilities.noString(url)) - throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? - Element target = resolve(appContext, url, stack, errors, p); - if (target == null) - throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - focus = target; - } else if (d.equals("value") && focus.isPrimitive()) { - return focus; - } else { - List children = focus.getChildren(d); - if (children.isEmpty()) - throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - if (children.size() > 1) - throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); - focus = children.get(0); - p = p + "." + d; - } - } + private StructureDefinition getProfileForType(String type, List list) { + for (TypeRefComponent tr : list) { + String url = tr.getWorkingCode(); + if (!Utilities.isAbsoluteUrl(url)) + url = "http://hl7.org/fhir/StructureDefinition/" + url; + long t = System.nanoTime(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + sdTime = sdTime + (System.nanoTime() - t); + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + return sd; + } + return null; + } + + private Element getValueForDiscriminator(Object appContext, List errors, Element element, String discriminator, ElementDefinition criteria, NodeStack stack) throws FHIRException, IOException { + String p = stack.getLiteralPath() + "." + element.getName(); + Element focus = element; + String[] dlist = discriminator.split("\\."); + for (String d : dlist) { + if (focus.fhirType().equals("Reference") && d.equals("reference")) { + String url = focus.getChildValue("reference"); + if (Utilities.noString(url)) + throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? + Element target = resolve(appContext, url, stack, errors, p); + if (target == null) + throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + focus = target; + } else if (d.equals("value") && focus.isPrimitive()) { return focus; + } else { + List children = focus.getChildren(d); + if (children.isEmpty()) + throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + if (children.size() > 1) + throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + focus = children.get(0); + p = p + "." + d; + } } + return focus; + } - private CodeSystem getCodeSystem(String system) { - long t = System.nanoTime(); - try { - return context.fetchCodeSystem(system); - } finally { - txTime = txTime + (System.nanoTime() - t); + private CodeSystem getCodeSystem(String system) { + long t = System.nanoTime(); + try { + return context.fetchCodeSystem(system); + } finally { + txTime = txTime + (System.nanoTime() - t); + } + } + + private boolean hasTime(String fmt) { + return fmt.contains("T"); + } + + private boolean hasTimeZone(String fmt) { + return fmt.length() > 10 && (fmt.substring(10).contains("-") || fmt.substring(10).contains("+") || fmt.substring(10).contains("Z")); + } + + private boolean isAbsolute(String uri) { + return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:uuid:") || uri.startsWith("urn:oid:") || uri.startsWith("urn:ietf:") + || uri.startsWith("urn:iso:") || uri.startsWith("urn:iso-astm:") || isValidFHIRUrn(uri); + } + + private boolean isValidFHIRUrn(String uri) { + return (uri.equals("urn:x-fhir:uk:id:nhs-number")) || uri.startsWith("urn:"); // Anyone can invent a URN, so why should we complain? + } + + public boolean isAnyExtensionsAllowed() { + return anyExtensionsAllowed; + } + + public boolean isErrorForUnknownProfiles() { + return errorForUnknownProfiles; + } + + public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) { + this.errorForUnknownProfiles = errorForUnknownProfiles; + } + + private boolean isParametersEntry(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("resource") && (pathEntryHasName(parts[parts.length - 2], "parameter") || pathEntryHasName(parts[parts.length - 2], "part")); + } + + private boolean isBundleEntry(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("resource") && pathEntryHasName(parts[parts.length - 2], "entry"); + } + + private boolean isBundleOutcome(String path) { + String[] parts = path.split("\\."); + return parts.length > 2 && parts[parts.length - 1].equals("outcome") && pathEntryHasName(parts[parts.length - 2], "response"); + } + + + private static boolean pathEntryHasName(String thePathEntry, String theName) { + if (thePathEntry.equals(theName)) { + return true; + } + if (thePathEntry.length() >= theName.length() + 3) { + if (thePathEntry.startsWith(theName)) { + if (thePathEntry.charAt(theName.length()) == '[') { + return true; } + } } + return false; + } - private boolean hasTime(String fmt) { - return fmt.contains("T"); - } + public boolean isPrimitiveType(String code) { + StructureDefinition sd = context.fetchTypeDefinition(code); + return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; + } - private boolean hasTimeZone(String fmt) { - return fmt.length() > 10 && (fmt.substring(10).contains("-") || fmt.substring(10).contains("+") || fmt.substring(10).contains("Z")); - } + private String getErrorMessage(String message) { + return message != null ? " (error message = " + message + ")" : ""; + } - private boolean isAbsolute(String uri) { - return Utilities.noString(uri) || uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("urn:uuid:") || uri.startsWith("urn:oid:") || uri.startsWith("urn:ietf:") - || uri.startsWith("urn:iso:") || uri.startsWith("urn:iso-astm:") || isValidFHIRUrn(uri); - } + public boolean isSuppressLoincSnomedMessages() { + return suppressLoincSnomedMessages; + } - private boolean isValidFHIRUrn(String uri) { - return (uri.equals("urn:x-fhir:uk:id:nhs-number")) || uri.startsWith("urn:"); // Anyone can invent a URN, so why should we complain? - } + private boolean nameMatches(String name, String tail) { + if (tail.endsWith("[x]")) + return name.startsWith(tail.substring(0, tail.length() - 3)); + else + return (name.equals(tail)); + } - public boolean isAnyExtensionsAllowed() { - return anyExtensionsAllowed; - } - - public boolean isErrorForUnknownProfiles() { - return errorForUnknownProfiles; - } - - public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) { - this.errorForUnknownProfiles = errorForUnknownProfiles; - } - - private boolean isParametersEntry(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("resource") && (pathEntryHasName(parts[parts.length - 2], "parameter") || pathEntryHasName(parts[parts.length - 2], "part")); - } - - private boolean isBundleEntry(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("resource") && pathEntryHasName(parts[parts.length - 2], "entry"); - } - - private boolean isBundleOutcome(String path) { - String[] parts = path.split("\\."); - return parts.length > 2 && parts[parts.length - 1].equals("outcome") && pathEntryHasName(parts[parts.length - 2], "response"); - } - - - private static boolean pathEntryHasName(String thePathEntry, String theName) { - if (thePathEntry.equals(theName)) { - return true; - } - if (thePathEntry.length() >= theName.length() + 3) { - if (thePathEntry.startsWith(theName)) { - if (thePathEntry.charAt(theName.length()) == '[') { - return true; - } - } - } - return false; - } - - public boolean isPrimitiveType(String code) { - StructureDefinition sd = context.fetchTypeDefinition(code); - return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; - } - - private String getErrorMessage(String message) { - return message != null ? " (error message = " + message + ")" : ""; - } - - public boolean isSuppressLoincSnomedMessages() { - return suppressLoincSnomedMessages; - } - - private boolean nameMatches(String name, String tail) { - if (tail.endsWith("[x]")) - return name.startsWith(tail.substring(0, tail.length() - 3)); + private boolean passesCodeWhitespaceRules(String v) { + if (!v.trim().equals(v)) + return false; + boolean lastWasSpace = true; + for (char c : v.toCharArray()) { + if (c == ' ') { + if (lastWasSpace) + return false; else - return (name.equals(tail)); + lastWasSpace = true; + } else if (Character.isWhitespace(c)) + return false; + else + lastWasSpace = false; } + return true; + } - private boolean passesCodeWhitespaceRules(String v) { - if (!v.trim().equals(v)) - return false; - boolean lastWasSpace = true; - for (char c : v.toCharArray()) { - if (c == ' ') { - if (lastWasSpace) - return false; - else - lastWasSpace = true; - } else if (Character.isWhitespace(c)) - return false; - else - lastWasSpace = false; + private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element hostContext, Element source) { + if (ref.startsWith("#")) { + // work back through the parent list. + // 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 + while (stack != null && stack.getElement() != null) { + if (stack.getElement().getProperty().isResource()) { + // ok, we'll try to find the contained reference + IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); + if (res != null) { + ResolvedReference rr = new ResolvedReference(); + rr.setResource(stack.getElement()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } } - return true; - } - - private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element hostContext, Element source) { - if (ref.startsWith("#")) { - // work back through the parent list. - // 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 - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getProperty().isResource()) { - // ok, we'll try to find the contained reference - IndexedElement res = getContainedById(stack.getElement(), ref.substring(1)); - if (res != null) { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(stack.getElement()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(stack.push(res.getMatch(), res.getIndex(), res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY) { - return null; // we don't try to resolve contained references across this boundary - } - stack = stack.parent; - } + if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY) { + return null; // we don't try to resolve contained references across this boundary + } + stack = stack.parent; + } + return null; + } else { + // work back through the parent list - if any of them are bundles, try to resolve + // the resource in the bundle + String fullUrl = null; // we're going to try to work this out as we go up + while (stack != null && stack.getElement() != null) { + if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.parent != null && stack.parent.getElement().getName().equals("entry")) { + String type = stack.parent.parent.element.getChildValue("type"); + fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary + if (fullUrl == null) + rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), + Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFULLURL); + } + if ("Bundle".equals(stack.getElement().getType())) { + String type = stack.getElement().getChildValue("type"); + IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type)); + if (res == null) { return null; + } else { + ResolvedReference rr = new ResolvedReference(); + rr.setResource(res.getMatch()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), + res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, + res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } + } + stack = stack.parent; + } + // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. + if (hostContext != null && "Bundle".equals(hostContext.fhirType())) { + String type = hostContext.getChildValue("type"); + Element entry = getEntryForSource(hostContext, source); + fullUrl = entry.getChildValue("fullUrl"); + IndexedElement res = getFromBundle(hostContext, ref, fullUrl, errors, path, type, "transaction".equals(type)); + if (res == null) { + return null; } else { - // work back through the parent list - if any of them are bundles, try to resolve - // the resource in the bundle - String fullUrl = null; // we're going to try to work this out as we go up - while (stack != null && stack.getElement() != null) { - if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.parent != null && stack.parent.getElement().getName().equals("entry")) { - String type = stack.parent.parent.element.getChildValue("type"); - fullUrl = stack.parent.getElement().getChildValue("fullUrl"); // we don't try to resolve contained references across this boundary - if (fullUrl == null) - rule(errors, IssueType.REQUIRED, stack.parent.getElement().line(), stack.parent.getElement().col(), stack.parent.getLiteralPath(), - Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, "Bundle entry missing fullUrl"); - } - if ("Bundle".equals(stack.getElement().getType())) { - String type = stack.getElement().getChildValue("type"); - IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type)); - if (res == null) { - return null; - } else { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(res.getMatch()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), - res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, - res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } - stack = stack.parent; - } - // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. - if (hostContext != null && "Bundle".equals(hostContext.fhirType())) { - String type = hostContext.getChildValue("type"); - Element entry = getEntryForSource(hostContext, source); - fullUrl = entry.getChildValue("fullUrl"); - IndexedElement res = getFromBundle(hostContext, ref, fullUrl, errors, path, type, "transaction".equals(type)); - if (res == null) { - return null; - } else { - ResolvedReference rr = new ResolvedReference(); - rr.setResource(res.getMatch()); - rr.setFocus(res.getMatch()); - rr.setExternal(false); - rr.setStack(new NodeStack(hostContext).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), - res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, - res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); - return rr; - } - } + ResolvedReference rr = new ResolvedReference(); + rr.setResource(res.getMatch()); + rr.setFocus(res.getMatch()); + rr.setExternal(false); + rr.setStack(new NodeStack(hostContext).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), + res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, + res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); + return rr; + } + } + } + return null; + } + + private Element getEntryForSource(Element bundle, Element element) { + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + for (Element entry : entries) { + if (entry.hasDescendant(element)) { + return entry; + } + } + return null; + } + + private ResolvedReference makeExternalRef(Element external, String path) { + ResolvedReference res = new ResolvedReference(); + res.setResource(external); + res.setFocus(external); + res.setExternal(true); + res.setStack(new NodeStack(external, path)); + return res; + } + + + private Element resolve(Object appContext, String ref, NodeStack stack, List errors, String path) throws IOException, FHIRException { + Element local = localResolve(ref, stack, errors, path, null, null).getFocus(); + if (local != null) + return local; + if (fetcher == null) + return null; + if (fetchCache.containsKey(ref)) { + return fetchCache.get(ref); + } else { + Element res = fetcher.fetch(appContext, ref); + fetchCache.put(ref, res); + return res; + } + } + + private ValueSet resolveBindingReference(DomainResource ctxt, String reference, String uri) { + if (reference != null) { + if (reference.startsWith("#")) { + for (Resource c : ctxt.getContained()) { + if (c.getId().equals(reference.substring(1)) && (c instanceof ValueSet)) + return (ValueSet) c; } return null; - } + } else { + long t = System.nanoTime(); + ValueSet fr = context.fetchResource(ValueSet.class, reference); + if (fr == null) { + if (!Utilities.isAbsoluteUrl(reference)) { + reference = resolve(uri, reference); + fr = context.fetchResource(ValueSet.class, reference); + } + } + if (fr == null) + fr = ValueSetUtilities.generateImplicitValueSet(reference); + txTime = txTime + (System.nanoTime() - t); + return fr; + } + } else + return null; + } - private Element getEntryForSource(Element bundle, Element element) { - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); + private String resolve(String uri, String ref) { + if (isBlank(uri)) { + return ref; + } + String[] up = uri.split("\\/"); + String[] rp = ref.split("\\/"); + if (context.getResourceNames().contains(up[up.length - 2]) && context.getResourceNames().contains(rp[0])) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < up.length - 2; i++) { + b.append(up[i]); + b.append("/"); + } + b.append(ref); + return b.toString(); + } else + return ref; + } + + private Element resolveInBundle(List entries, String ref, String fullUrl, String type, String id) { + if (Utilities.isAbsoluteUrl(ref)) { + // if the reference is absolute, then you resolve by fullUrl. No other thinking is required. + for (Element entry : entries) { + String fu = entry.getNamedChildValue("fullUrl"); + if (ref.equals(fu)) + return entry; + } + return null; + } else { + // split into base, type, and id + String u = null; + if (fullUrl != null && fullUrl.endsWith(type + "/" + id)) + // fullUrl = complex + u = fullUrl.substring(0, fullUrl.length() - (type + "/" + id).length()) + ref; +// u = fullUrl.substring((type+"/"+id).length())+ref; + String[] parts = ref.split("\\/"); + if (parts.length >= 2) { + String t = parts[0]; + String i = parts[1]; for (Element entry : entries) { - if (entry.hasDescendant(element)) { + String fu = entry.getNamedChildValue("fullUrl"); + if (fu != null && fu.equals(u)) + return entry; + if (u == null) { + Element resource = entry.getNamedChild("resource"); + if (resource != null) { + String et = resource.getType(); + String eid = resource.getNamedChildValue("id"); + if (t.equals(et) && i.equals(eid)) return entry; } + } } - return null; + } + return null; } + } - private ResolvedReference makeExternalRef(Element external, String path) { - ResolvedReference res = new ResolvedReference(); - res.setResource(external); - res.setFocus(external); - res.setExternal(true); - res.setStack(new NodeStack(external, path)); - return res; + private ElementDefinition resolveNameReference(StructureDefinitionSnapshotComponent snapshot, String contentReference) { + for (ElementDefinition ed : snapshot.getElement()) + if (contentReference.equals("#" + ed.getId())) + return ed; + return null; + } + + private StructureDefinition resolveProfile(StructureDefinition profile, String pr) { + if (pr.startsWith("#")) { + for (Resource r : profile.getContained()) { + if (r.getId().equals(pr.substring(1)) && r instanceof StructureDefinition) + return (StructureDefinition) r; + } + return null; + } else { + long t = System.nanoTime(); + StructureDefinition fr = context.fetchResource(StructureDefinition.class, pr); + sdTime = sdTime + (System.nanoTime() - t); + return fr; } + } - - private Element resolve(Object appContext, String ref, NodeStack stack, List errors, String path) throws IOException, FHIRException { - Element local = localResolve(ref, stack, errors, path, null, null).getFocus(); - if (local != null) - return local; - if (fetcher == null) - return null; - if (fetchCache.containsKey(ref)) { - return fetchCache.get(ref); - } else { - Element res = fetcher.fetch(appContext, ref); - fetchCache.put(ref, res); - return res; - } + private ElementDefinition resolveType(String type, List list) { + for (TypeRefComponent tr : list) { + String url = tr.getWorkingCode(); + if (!Utilities.isAbsoluteUrl(url)) + url = "http://hl7.org/fhir/StructureDefinition/" + url; + long t = System.nanoTime(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); + sdTime = sdTime + (System.nanoTime() - t); + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + return sd.getSnapshot().getElement().get(0); } + return null; + } - private ValueSet resolveBindingReference(DomainResource ctxt, String reference, String uri) { - if (reference != null) { - if (reference.startsWith("#")) { - for (Resource c : ctxt.getContained()) { - if (c.getId().equals(reference.substring(1)) && (c instanceof ValueSet)) - return (ValueSet) c; - } - return null; + public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) { + this.anyExtensionsAllowed = anyExtensionsAllowed; + } + + public IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value) { + bpWarnings = value; + return this; + } + + @Override + public void setCheckDisplay(CheckDisplayOption checkDisplay) { + this.checkDisplay = checkDisplay; + } + + public void setSuppressLoincSnomedMessages(boolean suppressLoincSnomedMessages) { + this.suppressLoincSnomedMessages = suppressLoincSnomedMessages; + } + + public IdStatus getResourceIdRule() { + return resourceIdRule; + } + + public void setResourceIdRule(IdStatus resourceIdRule) { + this.resourceIdRule = resourceIdRule; + } + + + public boolean isAllowXsiLocation() { + return allowXsiLocation; + } + + public void setAllowXsiLocation(boolean allowXsiLocation) { + this.allowXsiLocation = allowXsiLocation; + } + + /** + * @param element - the candidate that might be in the slice + * @param path - for reporting any errors. the XPath for the element + * @param slicer - the definition of how slicing is determined + * @param ed - the slice for which to test membership + * @param errors + * @param stack + * @return + * @throws DefinitionException + * @throws DefinitionException + * @throws IOException + * @throws FHIRException + */ + private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List errors, List sliceInfo, NodeStack stack) throws DefinitionException, FHIRException { + if (!slicer.getSlicing().hasDiscriminator()) + return false; // cannot validate in this case + + ExpressionNode n = (ExpressionNode) ed.getUserData("slice.expression.cache"); + if (n == null) { + long t = System.nanoTime(); + // GG: this approach is flawed because it treats discriminators individually rather than collectively + StringBuilder expression = new StringBuilder("true"); + boolean anyFound = false; + Set discriminators = new HashSet<>(); + for (ElementDefinitionSlicingDiscriminatorComponent s : slicer.getSlicing().getDiscriminator()) { + String discriminator = s.getPath(); + discriminators.add(discriminator); + + List criteriaElements = getCriteriaForDiscriminator(path, ed, discriminator, profile, s.getType() == DiscriminatorType.PROFILE); + boolean found = false; + for (ElementDefinition criteriaElement : criteriaElements) { + found = true; + if (s.getType() == DiscriminatorType.TYPE) { + String type = null; + if (!criteriaElement.getPath().contains("[") && discriminator.contains("[")) { + discriminator = discriminator.substring(0, discriminator.indexOf('[')); + String lastNode = tail(discriminator); + type = tail(criteriaElement.getPath()).substring(lastNode.length()); + type = type.substring(0, 1).toLowerCase() + type.substring(1); + } else if (!criteriaElement.hasType() || criteriaElement.getType().size() == 1) { + if (discriminator.contains("[")) + discriminator = discriminator.substring(0, discriminator.indexOf('[')); + type = criteriaElement.getType().get(0).getWorkingCode(); + } else if (criteriaElement.getType().size() > 1) { + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); + } else + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); + if (discriminator.isEmpty()) + expression.append(" and $this is " + type); + else + expression.append(" and " + discriminator + " is " + type); + } else if (s.getType() == DiscriminatorType.PROFILE) { + if (criteriaElement.getType().size() == 0) { + throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + } + if (criteriaElement.getType().size() != 1) { + throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + } + List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); + if (list.size() == 0) { + throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + } else if (list.size() > 1) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); + for (CanonicalType c : list) { + b.append(discriminator + ".conformsTo('" + c.getValue() + "')"); + } + expression.append(" and (" + b + ")"); } else { - long t = System.nanoTime(); - ValueSet fr = context.fetchResource(ValueSet.class, reference); - if (fr == null) { - if (!Utilities.isAbsoluteUrl(reference)) { - reference = resolve(uri, reference); - fr = context.fetchResource(ValueSet.class, reference); - } - } - if (fr == null) - fr = ValueSetUtilities.generateImplicitValueSet(reference); - txTime = txTime + (System.nanoTime() - t); - return fr; + expression.append(" and " + discriminator + ".conformsTo('" + list.get(0).getValue() + "')"); } - } else - return null; - } - - private String resolve(String uri, String ref) { - if (isBlank(uri)) { - return ref; - } - String[] up = uri.split("\\/"); - String[] rp = ref.split("\\/"); - if (context.getResourceNames().contains(up[up.length - 2]) && context.getResourceNames().contains(rp[0])) { - StringBuilder b = new StringBuilder(); - for (int i = 0; i < up.length - 2; i++) { - b.append(up[i]); - b.append("/"); - } - b.append(ref); - return b.toString(); - } else - return ref; - } - - private Element resolveInBundle(List entries, String ref, String fullUrl, String type, String id) { - if (Utilities.isAbsoluteUrl(ref)) { - // if the reference is absolute, then you resolve by fullUrl. No other thinking is required. - for (Element entry : entries) { - String fu = entry.getNamedChildValue("fullUrl"); - if (ref.equals(fu)) - return entry; - } - return null; - } else { - // split into base, type, and id - String u = null; - if (fullUrl != null && fullUrl.endsWith(type + "/" + id)) - // fullUrl = complex - u = fullUrl.substring(0, fullUrl.length() - (type + "/" + id).length()) + ref; -// u = fullUrl.substring((type+"/"+id).length())+ref; - String[] parts = ref.split("\\/"); - if (parts.length >= 2) { - String t = parts[0]; - String i = parts[1]; - for (Element entry : entries) { - String fu = entry.getNamedChildValue("fullUrl"); - if (fu != null && fu.equals(u)) - return entry; - if (u == null) { - Element resource = entry.getNamedChild("resource"); - if (resource != null) { - String et = resource.getType(); - String eid = resource.getNamedChildValue("id"); - if (t.equals(et) && i.equals(eid)) - return entry; - } - } - } - } - return null; - } - } - - private ElementDefinition resolveNameReference(StructureDefinitionSnapshotComponent snapshot, String contentReference) { - for (ElementDefinition ed : snapshot.getElement()) - if (contentReference.equals("#" + ed.getId())) - return ed; - return null; - } - - private StructureDefinition resolveProfile(StructureDefinition profile, String pr) { - if (pr.startsWith("#")) { - for (Resource r : profile.getContained()) { - if (r.getId().equals(pr.substring(1)) && r instanceof StructureDefinition) - return (StructureDefinition) r; - } - return null; - } else { - long t = System.nanoTime(); - StructureDefinition fr = context.fetchResource(StructureDefinition.class, pr); - sdTime = sdTime + (System.nanoTime() - t); - return fr; - } - } - - private ElementDefinition resolveType(String type, List list) { - for (TypeRefComponent tr : list) { - String url = tr.getWorkingCode(); - if (!Utilities.isAbsoluteUrl(url)) - url = "http://hl7.org/fhir/StructureDefinition/" + url; - long t = System.nanoTime(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); - sdTime = sdTime + (System.nanoTime() - t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) - return sd.getSnapshot().getElement().get(0); - } - return null; - } - - public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) { - this.anyExtensionsAllowed = anyExtensionsAllowed; - } - - public IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value) { - bpWarnings = value; - return this; - } - - @Override - public void setCheckDisplay(CheckDisplayOption checkDisplay) { - this.checkDisplay = checkDisplay; - } - - public void setSuppressLoincSnomedMessages(boolean suppressLoincSnomedMessages) { - this.suppressLoincSnomedMessages = suppressLoincSnomedMessages; - } - - public IdStatus getResourceIdRule() { - return resourceIdRule; - } - - public void setResourceIdRule(IdStatus resourceIdRule) { - this.resourceIdRule = resourceIdRule; - } - - - public boolean isAllowXsiLocation() { - return allowXsiLocation; - } - - public void setAllowXsiLocation(boolean allowXsiLocation) { - this.allowXsiLocation = allowXsiLocation; - } - - /** - * @param element - the candidate that might be in the slice - * @param path - for reporting any errors. the XPath for the element - * @param slicer - the definition of how slicing is determined - * @param ed - the slice for which to test membership - * @param errors - * @param stack - * @return - * @throws DefinitionException - * @throws DefinitionException - * @throws IOException - * @throws FHIRException - */ - private boolean sliceMatches(ValidatorHostContext hostContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List errors, List sliceInfo, NodeStack stack) throws DefinitionException, FHIRException { - if (!slicer.getSlicing().hasDiscriminator()) - return false; // cannot validate in this case - - ExpressionNode n = (ExpressionNode) ed.getUserData("slice.expression.cache"); - if (n == null) { - long t = System.nanoTime(); - // GG: this approach is flawed because it treats discriminators individually rather than collectively - StringBuilder expression = new StringBuilder("true"); - boolean anyFound = false; - Set discriminators = new HashSet<>(); - for (ElementDefinitionSlicingDiscriminatorComponent s : slicer.getSlicing().getDiscriminator()) { - String discriminator = s.getPath(); - discriminators.add(discriminator); - - List criteriaElements = getCriteriaForDiscriminator(path, ed, discriminator, profile, s.getType() == DiscriminatorType.PROFILE); - boolean found = false; - for (ElementDefinition criteriaElement : criteriaElements) { - found = true; - if (s.getType() == DiscriminatorType.TYPE) { - String type = null; - if (!criteriaElement.getPath().contains("[") && discriminator.contains("[")) { - discriminator = discriminator.substring(0, discriminator.indexOf('[')); - String lastNode = tail(discriminator); - type = tail(criteriaElement.getPath()).substring(lastNode.length()); - type = type.substring(0, 1).toLowerCase() + type.substring(1); - } else if (!criteriaElement.hasType() || criteriaElement.getType().size() == 1) { - if (discriminator.contains("[")) - discriminator = discriminator.substring(0, discriminator.indexOf('[')); - type = criteriaElement.getType().get(0).getWorkingCode(); - } else if (criteriaElement.getType().size() > 1) { - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); - } else - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); - if (discriminator.isEmpty()) - expression.append(" and $this is " + type); - else - expression.append(" and " + discriminator + " is " + type); - } else if (s.getType() == DiscriminatorType.PROFILE) { - if (criteriaElement.getType().size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } - if (criteriaElement.getType().size() != 1) { - throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } - List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); - if (list.size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); - } else if (list.size() > 1) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); - for (CanonicalType c : list) { - b.append(discriminator + ".conformsTo('" + c.getValue() + "')"); - } - expression.append(" and (" + b + ")"); - } else { - expression.append(" and " + discriminator + ".conformsTo('" + list.get(0).getValue() + "')"); - } - } else if (s.getType() == DiscriminatorType.EXISTS) { - if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1) - expression.append(" and (" + discriminator + ".exists())"); - else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) - expression.append(" and (" + discriminator + ".exists().not())"); - else - throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); - } else if (criteriaElement.hasFixed()) { - buildFixedExpression(ed, expression, discriminator, criteriaElement); - } else if (criteriaElement.hasPattern()) { - buildPattternExpression(ed, expression, discriminator, criteriaElement); - } else if (criteriaElement.hasBinding() && criteriaElement.getBinding().hasStrength() && criteriaElement.getBinding().getStrength().equals(BindingStrength.REQUIRED) && criteriaElement.getBinding().hasValueSet()) { - expression.append(" and (" + discriminator + " memberOf '" + criteriaElement.getBinding().getValueSet() + "')"); - } else { - found = false; - } - if (found) - break; - } - if (found) - anyFound = true; - } - if (!anyFound) { - if (slicer.getSlicing().getDiscriminator().size() > 1) - throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); - else - throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); - } - - try { - n = fpe.parse(fixExpr(expression.toString())); - } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); - } - fpeTime = fpeTime + (System.nanoTime() - t); - ed.setUserData("slice.expression.cache", n); - } - - ValidatorHostContext shc = hostContext.forSlicing(); - boolean pass = evaluateSlicingExpression(shc, element, path, profile, n); - if (!pass) { - slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Does not match slice'" + ed.getSliceName(), "discriminator = " + Utilities.escapeXml(n.toString())); - for (String url : shc.getSliceRecords().keySet()) { - slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + stack.getLiteralPath() + " against profile " + url, - "Profile " + url + " does not match for " + stack.getLiteralPath() + " because of the following profile issues: " + errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))); - } - } - return pass; - } - - public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException { - String msg; - boolean ok; - try { - long t = System.nanoTime(); - ok = fpe.evaluateToBoolean(hostContext.forProfile(profile), hostContext.getResource(), hostContext.getRootResource(), element, n); - fpeTime = fpeTime + (System.nanoTime() - t); - msg = fpe.forLog(); - } catch (Exception ex) { - ex.printStackTrace(); - throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); - } - return ok; - } - - private void buildPattternExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { - DataType pattern = criteriaElement.getPattern(); - if (pattern instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) pattern; - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, discriminator, cc); - } else if (pattern instanceof Coding) { - Coding c = (Coding) pattern; - expression.append(" and "); - buildCodingExpression(ed, expression, discriminator, c); - } else if (pattern instanceof Identifier) { - Identifier ii = (Identifier) pattern; - expression.append(" and "); - buildIdentifierExpression(ed, expression, discriminator, ii); - } else - throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); - } - - private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) - throws DefinitionException { - if (ii.hasExtension()) - throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - boolean first = true; - expression.append(discriminator + ".where("); - if (ii.hasSystem()) { - first = false; - expression.append("system = '" + ii.getSystem() + "'"); - } - if (ii.hasValue()) { - if (first) - first = false; + } else if (s.getType() == DiscriminatorType.EXISTS) { + if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1) + expression.append(" and (" + discriminator + ".exists())"); + else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) + expression.append(" and (" + discriminator + ".exists().not())"); else - expression.append(" and "); - expression.append("value = '" + ii.getValue() + "'"); + throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); + } else if (criteriaElement.hasFixed()) { + buildFixedExpression(ed, expression, discriminator, criteriaElement); + } else if (criteriaElement.hasPattern()) { + buildPattternExpression(ed, expression, discriminator, criteriaElement); + } else if (criteriaElement.hasBinding() && criteriaElement.getBinding().hasStrength() && criteriaElement.getBinding().getStrength().equals(BindingStrength.REQUIRED) && criteriaElement.getBinding().hasValueSet()) { + expression.append(" and (" + discriminator + " memberOf '" + criteriaElement.getBinding().getValueSet() + "')"); + } else { + found = false; + } + if (found) + break; } - if (ii.hasUse()) { - if (first) - first = false; - else - expression.append(" and "); - expression.append("use = '" + ii.getUse() + "'"); - } - if (ii.hasType()) { - if (first) - first = false; - else - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, "type", ii.getType()); - } - expression.append(").exists()"); + if (found) + anyFound = true; + } + if (!anyFound) { + if (slicer.getSlicing().getDiscriminator().size() > 1) + throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); + else + throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); + } + + try { + n = fpe.parse(fixExpr(expression.toString())); + } catch (FHIRLexerException e) { + throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + } + fpeTime = fpeTime + (System.nanoTime() - t); + ed.setUserData("slice.expression.cache", n); } - private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) - throws DefinitionException { - if (cc.hasText()) - throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (!cc.hasCoding()) - throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (cc.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - boolean firstCoding = true; - for (Coding c : cc.getCoding()) { - if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - if (firstCoding) firstCoding = false; - else expression.append(" and "); - expression.append(discriminator + ".coding.where("); - boolean first = true; - if (c.hasSystem()) { - first = false; - expression.append("system = '" + c.getSystem() + "'"); - } - if (c.hasVersion()) { - if (first) first = false; - else expression.append(" and "); - expression.append("version = '" + c.getVersion() + "'"); - } - if (c.hasCode()) { - if (first) first = false; - else expression.append(" and "); - expression.append("code = '" + c.getCode() + "'"); - } - if (c.hasDisplay()) { - if (first) first = false; - else expression.append(" and "); - expression.append("display = '" + c.getDisplay() + "'"); - } - expression.append(").exists()"); - } + ValidatorHostContext shc = hostContext.forSlicing(); + boolean pass = evaluateSlicingExpression(shc, element, path, profile, n); + if (!pass) { + slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Does not match slice'" + ed.getSliceName(), "discriminator = " + Utilities.escapeXml(n.toString())); + for (String url : shc.getSliceRecords().keySet()) { + slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, "Details for " + stack.getLiteralPath() + " against profile " + url, + "Profile " + url + " does not match for " + stack.getLiteralPath() + " because of the following profile issues: " + errorSummaryForSlicingAsHtml(shc.getSliceRecords().get(url))); + } } + return pass; + } - private void buildCodingExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Coding c) - throws DefinitionException { + public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException { + String msg; + boolean ok; + try { + long t = System.nanoTime(); + ok = fpe.evaluateToBoolean(hostContext.forProfile(profile), hostContext.getResource(), hostContext.getRootResource(), element, n); + fpeTime = fpeTime + (System.nanoTime() - t); + msg = fpe.forLog(); + } catch (Exception ex) { + ex.printStackTrace(); + throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); + } + return ok; + } + + private void buildPattternExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { + DataType pattern = criteriaElement.getPattern(); + if (pattern instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) pattern; + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, discriminator, cc); + } else if (pattern instanceof Coding) { + Coding c = (Coding) pattern; + expression.append(" and "); + buildCodingExpression(ed, expression, discriminator, c); + } else if (pattern instanceof Identifier) { + Identifier ii = (Identifier) pattern; + expression.append(" and "); + buildIdentifierExpression(ed, expression, discriminator, ii); + } else + throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); + } + + private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) + throws DefinitionException { + if (ii.hasExtension()) + throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + boolean first = true; + expression.append(discriminator + ".where("); + if (ii.hasSystem()) { + first = false; + expression.append("system = '" + ii.getSystem() + "'"); + } + if (ii.hasValue()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("value = '" + ii.getValue() + "'"); + } + if (ii.hasUse()) { + if (first) + first = false; + else + expression.append(" and "); + expression.append("use = '" + ii.getUse() + "'"); + } + if (ii.hasType()) { + if (first) + first = false; + else + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, "type", ii.getType()); + } + expression.append(").exists()"); + } + + private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) + throws DefinitionException { + if (cc.hasText()) + throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); + if (!cc.hasCoding()) + throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); + if (cc.hasExtension()) + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + boolean firstCoding = true; + for (Coding c : cc.getCoding()) { if (c.hasExtension()) throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); - expression.append(discriminator + ".where("); + if (firstCoding) firstCoding = false; + else expression.append(" and "); + expression.append(discriminator + ".coding.where("); boolean first = true; if (c.hasSystem()) { first = false; @@ -3321,2302 +3257,2326 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } expression.append(").exists()"); } + } - private void buildFixedExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { - DataType fixed = criteriaElement.getFixed(); - if (fixed instanceof CodeableConcept) { - CodeableConcept cc = (CodeableConcept) fixed; - expression.append(" and "); - buildCodeableConceptExpression(ed, expression, discriminator, cc); - } else if (fixed instanceof Identifier) { - Identifier ii = (Identifier) fixed; - expression.append(" and "); - buildIdentifierExpression(ed, expression, discriminator, ii); - } else if (fixed instanceof Coding) { - Coding c = (Coding) fixed; - expression.append(" and "); - buildCodingExpression(ed, expression, discriminator, c); - } else { - expression.append(" and ("); - if (fixed instanceof StringType) { - Gson gson = new Gson(); - String json = gson.toJson((StringType) fixed); - String escapedString = json.substring(json.indexOf(":") + 2); - escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); - expression.append("'" + escapedString + "'"); - } else if (fixed instanceof UriType) { - expression.append("'" + ((UriType) fixed).asStringValue() + "'"); - } else if (fixed instanceof IntegerType) { - expression.append(((IntegerType) fixed).asStringValue()); - } else if (fixed instanceof DecimalType) { - expression.append(((IntegerType) fixed).asStringValue()); - } else if (fixed instanceof BooleanType) { - expression.append(((BooleanType) fixed).asStringValue()); - } else - throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); - expression.append(" in " + discriminator + ")"); - } + private void buildCodingExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Coding c) + throws DefinitionException { + if (c.hasExtension()) + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + expression.append(discriminator + ".where("); + boolean first = true; + if (c.hasSystem()) { + first = false; + expression.append("system = '" + c.getSystem() + "'"); } - - // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) - private void start(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { - checkLang(resource, stack); - - if ("Bundle".equals(element.fhirType())) { - resolveBundleReferences(element, new ArrayList()); - } - startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials()); - - List res = new ArrayList<>(); - Element meta = element.getNamedChild("meta"); - if (meta != null) { - List profiles = new ArrayList(); - meta.getNamedChildren("profile", profiles); - int i = 0; - for (Element profile : profiles) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); - if (!defn.getUrl().equals(profile.primitiveValue())) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, "Profile reference '" + profile.primitiveValue() + "' could not be resolved, so has not been checked")) { - startInner(hostContext, errors, resource, element, sd, stack, false); - } - } - i++; - } - } + if (c.hasVersion()) { + if (first) first = false; + else expression.append(" and "); + expression.append("version = '" + c.getVersion() + "'"); } - - private void resolveBundleReferences(Element element, List bundles) { - if (!element.hasUserData("validator.bundle.resolved")) { - element.setUserData("validator.bundle.resolved", true); - List list = new ArrayList(); - list.addAll(bundles); - list.add(0, element); - List entries = element.getChildrenByName("entry"); - for (Element entry : entries) { - String fu = entry.getChildValue("fullUrl"); - Element r = entry.getNamedChild("resource"); - if (r != null) { - resolveBundleReferencesInResource(list, r, fu); - } - } - } + if (c.hasCode()) { + if (first) first = false; + else expression.append(" and "); + expression.append("code = '" + c.getCode() + "'"); } - - private void resolveBundleReferencesInResource(List bundles, Element r, String fu) { - r.setUserData("validator.bundle.resolution-resource", null); - if ("Bundle".equals(r.fhirType())) { - resolveBundleReferences(r, bundles); - } else { - for (Element child : r.getChildren()) { - resolveBundleReferencesForElement(bundles, r, fu, child); - } - } + if (c.hasDisplay()) { + if (first) first = false; + else expression.append(" and "); + expression.append("display = '" + c.getDisplay() + "'"); } + expression.append(").exists()"); + } - private void resolveBundleReferencesForElement(List bundles, Element resource, String fu, Element element) { - if ("Reference".equals(element.fhirType())) { - String ref = element.getChildValue("reference"); - if (!Utilities.noString(ref)) { - for (Element bundle : bundles) { - List entries = bundle.getChildren("entry"); - Element tgt = resolveInBundle(entries, ref, fu, resource.fhirType(), resource.getIdBase()); - if (tgt != null) { - element.setUserData("validator.bundle.resolution", tgt.getNamedChild("resource")); - return; - } - } - element.setUserData("validator.bundle.resolution-failed", ref); - } - } else { - element.setUserData("validator.bundle.resolution-noref", null); - for (Element child : element.getChildren()) { - resolveBundleReferencesForElement(bundles, resource, fu, child); - } - } - + private void buildFixedExpression(ElementDefinition ed, StringBuilder expression, String discriminator, ElementDefinition criteriaElement) throws DefinitionException { + DataType fixed = criteriaElement.getFixed(); + if (fixed instanceof CodeableConcept) { + CodeableConcept cc = (CodeableConcept) fixed; + expression.append(" and "); + buildCodeableConceptExpression(ed, expression, discriminator, cc); + } else if (fixed instanceof Identifier) { + Identifier ii = (Identifier) fixed; + expression.append(" and "); + buildIdentifierExpression(ed, expression, discriminator, ii); + } else if (fixed instanceof Coding) { + Coding c = (Coding) fixed; + expression.append(" and "); + buildCodingExpression(ed, expression, discriminator, c); + } else { + expression.append(" and ("); + if (fixed instanceof StringType) { + Gson gson = new Gson(); + String json = gson.toJson((StringType) fixed); + String escapedString = json.substring(json.indexOf(":") + 2); + escapedString = escapedString.substring(0, escapedString.indexOf(",\"myStringValue") - 1); + expression.append("'" + escapedString + "'"); + } else if (fixed instanceof UriType) { + expression.append("'" + ((UriType) fixed).asStringValue() + "'"); + } else if (fixed instanceof IntegerType) { + expression.append(((IntegerType) fixed).asStringValue()); + } else if (fixed instanceof DecimalType) { + expression.append(((IntegerType) fixed).asStringValue()); + } else if (fixed instanceof BooleanType) { + expression.append(((BooleanType) fixed).asStringValue()); + } else + throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); + expression.append(" in " + discriminator + ")"); } + } - public void startInner(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials) { - // the first piece of business is to see if we've validated this resource against this profile before. - // if we have (*or if we still are*), then we'll just return our existing errors - ResourceValidationTracker resTracker = getResourceTracker(element); - List cachedErrors = resTracker.getOutcomes(defn); - if (cachedErrors != null) { - for (ValidationMessage vm : cachedErrors) { - if (!errors.contains(vm)) { - errors.add(vm); - } - } + // checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up) + private void start(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException { + checkLang(resource, stack); + + if ("Bundle".equals(element.fhirType())) { + resolveBundleReferences(element, new ArrayList()); + } + startInner(hostContext, errors, resource, element, defn, stack, hostContext.isCheckSpecials()); + + List res = new ArrayList<>(); + Element meta = element.getNamedChild("meta"); + if (meta != null) { + List profiles = new ArrayList(); + meta.getNamedChildren("profile", profiles); + int i = 0; + for (Element profile : profiles) { + StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile.primitiveValue()); + if (!defn.getUrl().equals(profile.primitiveValue())) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath() + ".meta.profile[" + i + "]", sd != null, I18nConstants.VALIDATION_VAL_PROFILE_UNKNOWN, profile.primitiveValue())) { + startInner(hostContext, errors, resource, element, sd, stack, false); + } + } + i++; + } + } + } + + private void resolveBundleReferences(Element element, List bundles) { + if (!element.hasUserData("validator.bundle.resolved")) { + element.setUserData("validator.bundle.resolved", true); + List list = new ArrayList(); + list.addAll(bundles); + list.add(0, element); + List entries = element.getChildrenByName("entry"); + for (Element entry : entries) { + String fu = entry.getChildValue("fullUrl"); + Element r = entry.getNamedChild("resource"); + if (r != null) { + resolveBundleReferencesInResource(list, r, fu); + } + } + } + } + + private void resolveBundleReferencesInResource(List bundles, Element r, String fu) { + r.setUserData("validator.bundle.resolution-resource", null); + if ("Bundle".equals(r.fhirType())) { + resolveBundleReferences(r, bundles); + } else { + for (Element child : r.getChildren()) { + resolveBundleReferencesForElement(bundles, r, fu, child); + } + } + } + + private void resolveBundleReferencesForElement(List bundles, Element resource, String fu, Element element) { + if ("Reference".equals(element.fhirType())) { + String ref = element.getChildValue("reference"); + if (!Utilities.noString(ref)) { + for (Element bundle : bundles) { + List entries = bundle.getChildren("entry"); + Element tgt = resolveInBundle(entries, ref, fu, resource.fhirType(), resource.getIdBase()); + if (tgt != null) { + element.setUserData("validator.bundle.resolution", tgt.getNamedChild("resource")); return; + } } - if (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")) { - List localErrors = new ArrayList(); - resTracker.startValidating(defn); - trackUsage(defn, hostContext, element); - validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null); - resTracker.storeOutcomes(defn, localErrors); - for (ValidationMessage vm : localErrors) { - if (!errors.contains(vm)) { - errors.add(vm); + element.setUserData("validator.bundle.resolution-failed", ref); + } + } else { + element.setUserData("validator.bundle.resolution-noref", null); + for (Element child : element.getChildren()) { + resolveBundleReferencesForElement(bundles, resource, fu, child); + } + } + + } + + public void startInner(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials) { + // the first piece of business is to see if we've validated this resource against this profile before. + // if we have (*or if we still are*), then we'll just return our existing errors + ResourceValidationTracker resTracker = getResourceTracker(element); + List cachedErrors = resTracker.getOutcomes(defn); + if (cachedErrors != null) { + for (ValidationMessage vm : cachedErrors) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + return; + } + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), defn.hasSnapshot(), I18nConstants.VALIDATION_VAL_PROFILE_NOSNAPSHOT)) { + List localErrors = new ArrayList(); + resTracker.startValidating(defn); + trackUsage(defn, hostContext, element); + validateElement(hostContext, localErrors, defn, defn.getSnapshot().getElement().get(0), null, null, resource, element, element.getName(), stack, false, true, null); + resTracker.storeOutcomes(defn, localErrors); + for (ValidationMessage vm : localErrors) { + if (!errors.contains(vm)) { + errors.add(vm); + } + } + } + if (checkSpecials) { + checkSpecials(hostContext, errors, element, stack, checkSpecials); + validateResourceRules(errors, element, stack); + } + } + + public void checkSpecials(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack, boolean checkSpecials) { + // specific known special validations + if (element.getType().equals("Bundle")) { + validateBundle(errors, element, stack, checkSpecials); + } else if (element.getType().equals("Observation")) { + validateObservation(errors, element, stack); + } else if (element.getType().equals("Questionnaire")) { + ArrayList parents = new ArrayList<>(); + parents.add(element); + validateQuestionannaireItem(errors, element, element, stack, parents); + } else if (element.getType().equals("QuestionnaireResponse")) { + validateQuestionannaireResponse(hostContext, errors, element, stack); + } else if (element.getType().equals("CapabilityStatement")) { + validateCapabilityStatement(errors, element, stack); + } else if (element.getType().equals("CodeSystem")) { + validateCodeSystem(errors, element, stack); + } + } + + private ResourceValidationTracker getResourceTracker(Element element) { + ResourceValidationTracker res = resourceTracker.get(element); + if (res == null) { + res = new ResourceValidationTracker(); + resourceTracker.put(element, res); + } + return res; + } + + private void validateQuestionannaireItem(List errors, Element element, Element questionnaire, NodeStack stack, List parents) { + List list = getItems(element); + for (int i = 0; i < list.size(); i++) { + Element e = list.get(i); + NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); + validateQuestionnaireElement(errors, ns, questionnaire, e, parents); + List np = new ArrayList(); + np.add(e); + np.addAll(parents); + validateQuestionannaireItem(errors, e, questionnaire, ns, np); + } + } + + private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List parents) { + // R4+ + if ((FHIRVersion.isR4Plus(context.getVersion())) && (item.hasChildren("enableWhen"))) { + List ewl = item.getChildren("enableWhen"); + for (Element ew : ewl) { + String ql = ew.getNamedChildValue("question"); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, I18nConstants.QUESTIONNAIRE_Q_ENABLEWHEN_NOLINK)) { + Element tgt = getQuestionById(item, ql); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, I18nConstants.QUESTIONNAIRE_Q_ENABLEWHEN_ISINNER)) { + tgt = getQuestionById(questionnaire, ql); + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, I18nConstants.QUESTIONNAIRE_Q_ENABLEWHEN_NOTARGET, ql)) { + if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, I18nConstants.QUESTIONNAIRE_Q_ENABLEWHEN_SELF)) { + if (!isBefore(item, tgt, parents)) { + warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, I18nConstants.QUESTIONNAIRE_Q_ENABLEWHEN_AFTER, ql); } + } } + } } - if (checkSpecials) { - checkSpecials(hostContext, errors, element, stack, checkSpecials); - validateResourceRules(errors, element, stack); - } + } } + } - public void checkSpecials(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack, boolean checkSpecials) { - // specific known special validations - if (element.getType().equals("Bundle")) { - validateBundle(errors, element, stack, checkSpecials); - } else if (element.getType().equals("Observation")) { - validateObservation(errors, element, stack); - } else if (element.getType().equals("Questionnaire")) { - ArrayList parents = new ArrayList<>(); - parents.add(element); - validateQuestionannaireItem(errors, element, element, stack, parents); - } else if (element.getType().equals("QuestionnaireResponse")) { - validateQuestionannaireResponse(hostContext, errors, element, stack); - } else if (element.getType().equals("CapabilityStatement")) { - validateCapabilityStatement(errors, element, stack); - } else if (element.getType().equals("CodeSystem")) { - validateCodeSystem(errors, element, stack); - } + private boolean isBefore(Element item, Element tgt, List parents) { + // we work up the list, looking for tgt in the children of the parents + if (parents.contains(tgt)) { + // actually, if the target is a parent, that's automatically ok + return true; } - - private ResourceValidationTracker getResourceTracker(Element element) { - ResourceValidationTracker res = resourceTracker.get(element); - if (res == null) { - res = new ResourceValidationTracker(); - resourceTracker.put(element, res); - } - return res; + for (Element p : parents) { + int i = findIndex(p, item); + int t = findIndex(p, tgt); + if (i > -1 && t > -1) { + return i > t; + } } + return false; // unsure... shouldn't ever get to this point; + } - private void validateQuestionannaireItem(List errors, Element element, Element questionnaire, NodeStack stack, List parents) { - List list = getItems(element); - for (int i = 0; i < list.size(); i++) { - Element e = list.get(i); - NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - validateQuestionnaireElement(errors, ns, questionnaire, e, parents); - List np = new ArrayList(); - np.add(e); - np.addAll(parents); - validateQuestionannaireItem(errors, e, questionnaire, ns, np); - } + + private int findIndex(Element parent, Element descendant) { + for (int i = 0; i < parent.getChildren().size(); i++) { + if (parent.getChildren().get(i) == descendant || isChild(parent.getChildren().get(i), descendant)) + return i; } + return -1; + } - private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List parents) { - // R4+ - if ((FHIRVersion.isR4Plus(context.getVersion())) && (item.hasChildren("enableWhen"))) { - List ewl = item.getChildren("enableWhen"); - for (Element ew : ewl) { - String ql = ew.getNamedChildValue("question"); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, ql != null, "Questions with an enableWhen must have a value for the question link")) { - Element tgt = getQuestionById(item, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt == null, "Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition")) { - tgt = getQuestionById(questionnaire, ql); - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != null, "Unable to find target '" + ql + "' for this question enableWhen")) { - if (rule(errors, IssueType.BUSINESSRULE, ns.literalPath, tgt != item, "Target for this question enableWhen can't reference itself")) { - if (!isBefore(item, tgt, parents)) { - warning(errors, IssueType.BUSINESSRULE, ns.literalPath, false, "The target of this enableWhen rule (" + ql + ") comes after the question itself"); - } - } - } - } - } - } - } - } - - private boolean isBefore(Element item, Element tgt, List parents) { - // we work up the list, looking for tgt in the children of the parents - if (parents.contains(tgt)) { - // actually, if the target is a parent, that's automatically ok - return true; - } - for (Element p : parents) { - int i = findIndex(p, item); - int t = findIndex(p, tgt); - if (i > -1 && t > -1) { - return i > t; - } - } - return false; // unsure... shouldn't ever get to this point; - } - - - private int findIndex(Element parent, Element descendant) { - for (int i = 0; i < parent.getChildren().size(); i++) { - if (parent.getChildren().get(i) == descendant || isChild(parent.getChildren().get(i), descendant)) - return i; - } - return -1; - } - - private boolean isChild(Element element, Element descendant) { - for (Element e : element.getChildren()) { - if (e == descendant) - return true; - if (isChild(e, descendant)) - return true; - } - return false; - } - - private Element getQuestionById(Element focus, String ql) { - List list = getItems(focus); - for (Element item : list) { - String v = item.getNamedChildValue("linkId"); - if (ql.equals(v)) - return item; - Element tgt = getQuestionById(item, ql); - if (tgt != null) - return tgt; - } - return null; - - } - - private List getItems(Element element) { - List list = new ArrayList<>(); - element.getNamedChildren("item", list); - return list; - } - - private void checkLang(Element resource, NodeStack stack) { - String lang = resource.getNamedChildValue("language"); - if (!Utilities.noString(lang)) - stack.workingLang = lang; - } - - private void validateResourceRules(List errors, Element element, NodeStack stack) { - String lang = element.getNamedChildValue("language"); - Element text = element.getNamedChild("text"); - if (text != null) { - Element div = text.getNamedChild("div"); - if (lang != null && div != null) { - XhtmlNode xhtml = div.getXhtml(); - String l = xhtml.getAttribute("lang"); - String xl = xhtml.getAttribute("xml:lang"); - if (l == null && xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else { - if (l == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else if (!l.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has a lang (" + l + "), but they differ "); - } - if (xl == null) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues)"); - } else if (!xl.equals(lang)) { - warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, "Resource has a language (" + lang + "), and the XHTML has an xml:lang (" + xl + "), but they differ "); - } - } - } - } - // security tags are a set (system|code) - Element meta = element.getNamedChild("meta"); - if (meta != null) { - Set tags = new HashSet<>(); - List list = new ArrayList<>(); - meta.getNamedChildren("security", list); - int i = 0; - for (Element e : list) { - String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); - rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s), "Duplicate Security Label " + s); - tags.add(s); - i++; - } - } - } - - private void validateCapabilityStatement(List errors, Element cs, NodeStack stack) { - int iRest = 0; - for (Element rest : cs.getChildrenByName("rest")) { - int iResource = 0; - for (Element resource : rest.getChildrenByName("resource")) { - int iSP = 0; - for (Element searchParam : resource.getChildrenByName("searchParam")) { - String ref = searchParam.getChildValue("definition"); - String type = searchParam.getChildValue("type"); - if (!Utilities.noString(ref)) { - SearchParameter sp = context.fetchResource(SearchParameter.class, ref); - if (sp != null) { - rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", - sp.getType().toCode().equals(type), "Type mismatch - SearchParameter '" + sp.getUrl() + "' type is " + sp.getType().toCode() + ", but type here is " + type); - } - } - iSP++; - } - iResource++; - } - iRest++; - } - } - - private void validateCodeSystem(List errors, Element cs, NodeStack stack) { - String url = cs.getNamedChildValue("url"); - String vsu = cs.getNamedChildValue("valueSet"); - if (!Utilities.noString(vsu)) { - ValueSet vs; - try { - vs = context.fetchResourceWithException(ValueSet.class, vsu); - } catch (FHIRException e) { - vs = null; - } - if (vs != null) { - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but it is an expansion")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a single include")) - if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but doesn't have a matching system (" + vs.getCompose().getInclude().get(0).getSystem() + ")")) { - rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() - && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), "CodeSystem " + url + " has a 'all system' value set of " + vsu + ", but the include has extra details"); - } - } - } // todo... try getting the value set the other way... - } - - private void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { - Element q = element.getNamedChild("questionnaire"); - String questionnaire = null; - if (q != null) { - /* - * q.getValue() is correct for R4 content, but we'll also accept the second - * option just in case we're validating raw STU3 content. Being lenient here - * isn't the end of the world since if someone is actually doing the reference - * wrong in R4 content it'll get flagged elsewhere by the validator too - */ - if (isNotBlank(q.getValue())) { - questionnaire = q.getValue(); - } else if (isNotBlank(q.getChildValue("reference"))) { - questionnaire = q.getChildValue("reference"); - } - } - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, "No questionnaire is identified, so no validation can be performed against the base questionnaire")) { - long t = System.nanoTime(); - Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); - sdTime = sdTime + (System.nanoTime() - t); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \"" + questionnaire + "\" could not be resolved, so no validation can be performed against the base questionnaire")) { - boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); - validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); - } - } - } - - private Questionnaire loadQuestionnaire(Element resource, String id) throws FHIRException { - try { - for (Element contained : resource.getChildren("contained")) { - if (contained.getIdBase().equals(id)) { - FhirPublication v = FhirPublication.fromCode(context.getVersion()); - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id); - byte[] json = bs.toByteArray(); - switch (v) { - case DSTU1: - throw new FHIRException("Unsupported version R1"); - case DSTU2: - org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); - Resource r5 = VersionConvertor_10_50.convertResource(r2); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case DSTU2016May: - org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); - r5 = VersionConvertor_14_50.convertResource(r2a); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case STU3: - org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); - r5 = VersionConvertor_30_50.convertResource(r3, false); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R4: - org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); - r5 = VersionConvertor_40_50.convertResource(r4); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R5: - r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - } - } - } - return null; - } catch (IOException e) { - throw new FHIRException(e); - } - } - - private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - String text = element.getNamedChildValue("text"); - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId " + qItem.getLinkId()); - - List answers = new ArrayList(); - element.getNamedChildren("answer", answers); - if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); - else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item " + qItem.getLinkId()); - } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled - // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? - // GDG 2019-07-13 -// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers), "Item has answer (2), even though it is not enabled "+qItem.getLinkId()); - } - - if (answers.size() > 1) - rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); - - for (Element answer : answers) { - NodeStack ns = stack.push(answer, -1, null, null); - if (qItem.getType() != null) { - switch (qItem.getType()) { - case GROUP: - rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); - break; - case DISPLAY: // nothing - break; - case BOOLEAN: - validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); - break; - case DECIMAL: - validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); - break; - case INTEGER: - validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); - break; - case DATE: - validateQuestionnaireResponseItemType(errors, answer, ns, "date"); - break; - case DATETIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); - break; - case TIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "time"); - break; - case STRING: - validateQuestionnaireResponseItemType(errors, answer, ns, "string"); - break; - case TEXT: - validateQuestionnaireResponseItemType(errors, answer, ns, "text"); - break; - case URL: - validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); - break; - case ATTACHMENT: - validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); - break; - case REFERENCE: - validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); - break; - case QUANTITY: - if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) - if (qItem.hasExtension("???")) - validateQuestionnaireResponseItemQuantity(errors, answer, ns); - break; - case CHOICE: - String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) - checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); - } - break; - case OPENCHOICE: - itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) - checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) - checkOption(errors, answer, ns, qsrc, qItem, "string", true); - } - break; -// case QUESTION: - case NULL: - // no validation - break; - } - } - validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); - } - if (qItem.getType() == null) { - fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item " + qItem.getLinkId() + " does not contain a type"); - } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { - List items = new ArrayList(); - element.getNamedChildren("item", items); - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); - } else { - validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); - } - } - - private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { - return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; - } - - private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - if (elements.size() > 1) - rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); - int i = 0; - for (Element element : elements) { - NodeStack ns = stack.push(element, i, null, null); - validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); - i++; - } - } - - private int getLinkIdIndex(List qItems, String linkId) { - for (int i = 0; i < qItems.size(); i++) { - if (linkId.equals(qItems.get(i).getLinkId())) - return i; - } - return -1; - } - - private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { - List items = new ArrayList(); - element.getNamedChildren("item", items); - // now, sort into stacks - Map> map = new HashMap>(); - int lastIndex = -1; - for (Element item : items) { - String linkId = item.getNamedChildValue("linkId"); - if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), "No LinkId, so can't be validated")) { - int index = getLinkIdIndex(qItems, linkId); - if (index == -1) { - QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); - if (qItem != null) { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); - NodeStack ns = stack.push(item, -1, null, null); - validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); - } else - rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \"" + linkId + "\" not found in questionnaire"); - } else { - rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); - lastIndex = index; - - // If an item has a child called "linkId" but no child called "answer", - // we'll treat it as not existing for the purposes of enableWhen validation - if (item.hasChildren("answer") || item.hasChildren("item")) { - List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); - mapItem.add(item); - } - } - } - } - - // ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order - for (QuestionnaireItemComponent qItem : qItems) { - List mapItem = map.get(qItem.getLinkId()); - validateQuestionnaireResponseItem(hostContext, qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); - } - } - - public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { - boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe); - if (mapItem != null) { - if (!enabled) { - int i = 0; - for (Element e : mapItem) { - NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); - rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, "Item has answer, even though it is not enabled (item id = '" + qItem.getLinkId() + "')"); - i++; - } - } - - // Recursively validate child items - validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); - - } else { - - // item is missing, is the question enabled? - if (enabled && qItem.getRequired()) { - String message = "No response found for required item with id = '" + qItem.getLinkId() + "'"; - if (inProgress) { - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); - } else { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); - } - } - - } - - } - - private String misplacedItemError(QuestionnaireItemComponent qItem) { - return qItem.hasLinkId() ? String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId()) : "Structural Error: item is in the wrong place"; - } - - private void validateQuestionnaireResponseItemQuantity(List errors, Element answer, NodeStack stack) { - - } - - private String validateQuestionnaireResponseItemType(List errors, Element element, NodeStack stack, String... types) { - List values = new ArrayList(); - element.getNamedChildrenWithWildcard("value[x]", values); - for (int i = 0; i < types.length; i++) { - if (types[i].equals("text")) { - types[i] = "string"; - } - } - if (values.size() > 0) { - NodeStack ns = stack.push(values.get(0), -1, null, null); - CommaSeparatedStringBuilder l = new CommaSeparatedStringBuilder(); - for (String s : types) { - l.append(s); - if (values.get(0).getName().equals("value" + Utilities.capitalize(s))) - return (s); - } - if (types.length == 1) - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be of type " + types[0]); - else - rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, "Answer value must be one of the types " + l.toString()); - } - return null; - } - - private QuestionnaireItemComponent findQuestionnaireItem(Questionnaire qSrc, String linkId) { - return findItem(qSrc.getItem(), linkId); - } - - private QuestionnaireItemComponent findItem(List list, String linkId) { - for (QuestionnaireItemComponent item : list) { - if (linkId.equals(item.getLinkId())) - return item; - QuestionnaireItemComponent result = findItem(item.getItem(), linkId); - if (result != null) - return result; - } - return null; - } - - private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { - ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); - if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, "ValueSet " + describeReference(ref) + " not found by validator")) { - try { - Coding c = ObjectConverter.readAsCoding(value); - if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { - if (theOpenChoice) { - return; - } - } - - long t = System.nanoTime(); - ValidationResult res = context.validateCode(new ValidationOptions(stack.workingLang), c, vs); - txTime = txTime + (System.nanoTime() - t); - if (!res.isOk()) { - txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire"); - } else if (res.getSeverity() != null) { - super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); - } - } catch (Exception e) { - warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "Error " + e.getMessage() + " validating Coding against Questionnaire Options"); - } - } - } - - private void validateAnswerCode(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) { - Element v = answer.getNamedChild("valueCoding"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) - checkCodingOption(errors, answer, stack, qSrc, qItem, theOpenChoice); - // validateAnswerCode(errors, v, stack, qItem.getOption()); - else if (qItem.hasAnswerValueSet()) - validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); - else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate options because no option or options are provided"); - } - - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { - checkOption(errors, answer, stack, qSrc, qItem, type, false); - } - - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { - if (type.equals("integer")) checkIntegerOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("date")) checkDateOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("time")) checkTimeOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("string")) checkStringOption(errors, answer, stack, qSrc, qItem, openChoice); - else if (type.equals("Coding")) checkCodingOption(errors, answer, stack, qSrc, qItem, openChoice); - } - - private void checkIntegerOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueInteger"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueIntegerType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type integer"); - } else { - boolean found = false; - for (IntegerType item : list) { - if (item.getValue() == Integer.parseInt(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The integer " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate integer answer option because no option list is provided"); - } - - private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueDate"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueDateType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type date"); - } else { - boolean found = false; - for (DateType item : list) { - if (item.getValue().equals(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The date " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate date answer option because no option list is provided"); - } - - private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueTime"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - list.add(components.getValueTimeType()); - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type time"); - } else { - boolean found = false; - for (TimeType item : list) { - if (item.getValue().equals(v.primitiveValue())) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The time " + v.primitiveValue() + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate time answer option because no option list is provided"); - } - - private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueString"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - if (components.getValue() != null) { - list.add(components.getValueStringType()); - } - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (!openChoice) { - if (list.isEmpty()) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type string"); - } else { - boolean found = false; - for (StringType item : list) { - if (item.getValue().equals((v.primitiveValue()))) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The string " + v.primitiveValue() + " is not a valid option"); - } - } - } - } else { - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate string answer option because no option list is provided"); - } - } - - private void checkCodingOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { - Element v = answer.getNamedChild("valueCoding"); - String system = v.getNamedChildValue("system"); - String code = v.getNamedChildValue("code"); - NodeStack ns = stack.push(v, -1, null, null); - if (qItem.getAnswerOption().size() > 0) { - List list = new ArrayList(); - for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { - try { - if (components.getValue() != null) { - list.add(components.getValueCoding()); - } - } catch (FHIRException e) { - // If it's the wrong type, just keep going - } - } - if (list.isEmpty() && !openChoice) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Option list has no option values of type coding"); - } else { - boolean found = false; - for (Coding item : list) { - if (ObjectUtil.equals(item.getSystem(), system) && ObjectUtil.equals(item.getCode(), code)) { - found = true; - break; - } - } - if (!found) { - rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, "The code " + system + "::" + code + " is not a valid option"); - } - } - } else - hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, "Cannot validate Coding option because no option list is provided"); - } - - private String tail(String path) { - return path.substring(path.lastIndexOf(".") + 1); - } - - private String tryParse(String ref) { - String[] parts = ref.split("\\/"); - switch (parts.length) { - case 1: - return null; - case 2: - return checkResourceType(parts[0]); - default: - if (parts[parts.length - 2].equals("_history")) - return checkResourceType(parts[parts.length - 4]); - else - return checkResourceType(parts[parts.length - 2]); - } - } - - private boolean typesAreAllReference(List theType) { - for (TypeRefComponent typeRefComponent : theType) { - if (typeRefComponent.getCode().equals("Reference") == false) { - return false; - } - } + private boolean isChild(Element element, Element descendant) { + for (Element e : element.getChildren()) { + if (e == descendant) + return true; + if (isChild(e, descendant)) return true; } + return false; + } - private void validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials) { - List entries = new ArrayList(); - bundle.getNamedChildren("entry", entries); - String type = bundle.getNamedChildValue("type"); - type = StringUtils.defaultString(type); + private Element getQuestionById(Element focus, String ql) { + List list = getItems(focus); + for (Element item : list) { + String v = item.getNamedChildValue("linkId"); + if (ql.equals(v)) + return item; + Element tgt = getQuestionById(item, ql); + if (tgt != null) + return tgt; + } + return null; - if (entries.size() == 0) { - rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); + } + + private List getItems(Element element) { + List list = new ArrayList<>(); + element.getNamedChildren("item", list); + return list; + } + + private void checkLang(Element resource, NodeStack stack) { + String lang = resource.getNamedChildValue("language"); + if (!Utilities.noString(lang)) + stack.workingLang = lang; + } + + private void validateResourceRules(List errors, Element element, NodeStack stack) { + String lang = element.getNamedChildValue("language"); + Element text = element.getNamedChild("text"); + if (text != null) { + Element div = text.getNamedChild("div"); + if (lang != null && div != null) { + XhtmlNode xhtml = div.getXhtml(); + String l = xhtml.getAttribute("lang"); + String xl = xhtml.getAttribute("xml:lang"); + if (l == null && xl == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING1); } else { - // Get the first entry, the MessageHeader - Element firstEntry = entries.get(0); - // Get the stack of the first entry - NodeStack firstStack = stack.push(firstEntry, 1, null, null); - - String fullUrl = firstEntry.getNamedChildValue("fullUrl"); - - if (type.equals("document")) { - Element resource = firstEntry.getNamedChild("resource"); - String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { - validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); - } - checkAllInterlinked(errors, entries, stack, bundle, true); - } - if (type.equals("message")) { - Element resource = firstEntry.getNamedChild("resource"); - String id = resource.getNamedChildValue("id"); - if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, "No resource on first entry")) { - validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); - } - checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); - } - // We do not yet have rules requiring that the id and fullUrl match when dealing with messaging Bundles - // validateResourceIds(errors, entries, stack); + if (l == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING2); + } else if (!l.equals(lang)) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT1, lang, l); + } + if (xl == null) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_MISSING3); + } else if (!xl.equals(lang)) { + warning(errors, IssueType.BUSINESSRULE, div.line(), div.col(), stack.getLiteralPath(), false, I18nConstants.LANGUAGE_XHTML_LANG_DIFFERENT2, lang, xl); + } } - for (Element entry : entries) { - String fullUrl = entry.getNamedChildValue("fullUrl"); - String url = getCanonicalURLForEntry(entry); - String id = getIdForEntry(entry); - if (url != null) { - if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless the resource id (" + id + ") also matches"); - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), "The canonical URL (" + url + ") cannot match the fullUrl (" + fullUrl + ") unless on the canonical server itself"); + } + } + // security tags are a set (system|code) + Element meta = element.getNamedChild("meta"); + if (meta != null) { + Set tags = new HashSet<>(); + List list = new ArrayList<>(); + meta.getNamedChildren("security", list); + int i = 0; + for (Element e : list) { + String s = e.getNamedChildValue("system") + "#" + e.getNamedChildValue("code"); + rule(errors, IssueType.BUSINESSRULE, e.line(), e.col(), stack.getLiteralPath() + ".meta.profile[" + Integer.toString(i) + "]", !tags.contains(s), I18nConstants.META_RES_SECURITY_DUPLICATE, s); + tags.add(s); + i++; + } + } + } + + private void validateCapabilityStatement(List errors, Element cs, NodeStack stack) { + int iRest = 0; + for (Element rest : cs.getChildrenByName("rest")) { + int iResource = 0; + for (Element resource : rest.getChildrenByName("resource")) { + int iSP = 0; + for (Element searchParam : resource.getChildrenByName("searchParam")) { + String ref = searchParam.getChildValue("definition"); + String type = searchParam.getChildValue("type"); + if (!Utilities.noString(ref)) { + SearchParameter sp = context.fetchResource(SearchParameter.class, ref); + if (sp != null) { + rule(errors, IssueType.INVALID, searchParam.line(), searchParam.col(), stack.literalPath + ".rest[" + iRest + "].resource[" + iResource + "].searchParam[" + iSP + "]", + sp.getType().toCode().equals(type), I18nConstants.CAPABALITYSTATEMENT_CS_SP_WRONGTYPE, sp.getUrl(), sp.getType().toCode(), type); } - // todo: check specials + } + iSP++; } + iResource++; + } + iRest++; } + } - // hack for pre-UTG v2/v3 - private boolean isV3orV2Url(String url) { - return url.startsWith("http://hl7.org/fhir/v3/") || url.startsWith("http://hl7.org/fhir/v2/"); - } - - public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; - private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; - private static final String EXECUTION_ID = "validator.execution.id"; - - private String uriRegexForVersion() { - if (VersionUtilities.isR3Ver(context.getVersion())) - return URI_REGEX3; - else - return Constants.URI_REGEX; - } - - private String getCanonicalURLForEntry(Element entry) { - Element e = entry.getNamedChild("resource"); - if (e == null) - return null; - return e.getNamedChildValue("url"); - } - - private String getIdForEntry(Element entry) { - Element e = entry.getNamedChild("resource"); - if (e == null) - return null; - return e.getNamedChildValue("id"); - } - - /** - * Check each resource entry to ensure that the entry's fullURL includes the resource's id - * value. Adds an ERROR ValidationMessge to errors List for a given entry if it references - * a resource and fullURL does not include the resource's id. - * - * @param errors List of ValidationMessage objects that new errors will be added to. - * @param entries List of entry Element objects to be checked. - * @param stack Current NodeStack used to create path names in error detail messages. - */ - private void validateResourceIds(List errors, List entries, NodeStack stack) { - // TODO: Need to handle _version - int i = 1; - for (Element entry : entries) { - String fullUrl = entry.getNamedChildValue("fullUrl"); - Element resource = entry.getNamedChild("resource"); - String id = resource != null ? resource.getNamedChildValue("id") : null; - if (id != null && fullUrl != null) { - String urlId = null; - if (fullUrl.startsWith("https://") || fullUrl.startsWith("http://")) { - urlId = fullUrl.substring(fullUrl.lastIndexOf('/') + 1); - } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { - urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); - } - rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id), - "Resource ID does not match the ID in the entry full URL ('" + id + "' vs '" + fullUrl + "') "); + private void validateCodeSystem(List errors, Element cs, NodeStack stack) { + String url = cs.getNamedChildValue("url"); + String vsu = cs.getNamedChildValue("valueSet"); + if (!Utilities.noString(vsu)) { + ValueSet vs; + try { + vs = context.fetchResourceWithException(ValueSet.class, vsu); + } catch (FHIRException e) { + vs = null; + } + if (vs != null) { + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), I18nConstants.CODESYSTEM_CS_VS_MISMATCH, url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, I18nConstants.CODESYSTEM_CS_VS_INVALID, url, vsu)) + if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), I18nConstants.CODESYSTEM_CS_VS_WRONGSYSTEM, url, vsu, vs.getCompose().getInclude().get(0).getSystem())) { + rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() + && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu); } - i++; + } + } // todo... try getting the value set the other way... + } + + private void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { + Element q = element.getNamedChild("questionnaire"); + String questionnaire = null; + if (q != null) { + /* + * q.getValue() is correct for R4 content, but we'll also accept the second + * option just in case we're validating raw STU3 content. Being lenient here + * isn't the end of the world since if someone is actually doing the reference + * wrong in R4 content it'll get flagged elsewhere by the validator too + */ + if (isNotBlank(q.getValue())) { + questionnaire = q.getValue(); + } else if (isNotBlank(q.getChildValue("reference"))) { + questionnaire = q.getChildValue("reference"); + } + } + if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE)) { + long t = System.nanoTime(); + Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); + sdTime = sdTime + (System.nanoTime() - t); + if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire)) { + boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); + validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); + } + } + } + + private Questionnaire loadQuestionnaire(Element resource, String id) throws FHIRException { + try { + for (Element contained : resource.getChildren("contained")) { + if (contained.getIdBase().equals(id)) { + FhirPublication v = FhirPublication.fromCode(context.getVersion()); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id); + byte[] json = bs.toByteArray(); + switch (v) { + case DSTU1: + throw new FHIRException("Unsupported version R1"); + case DSTU2: + org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); + Resource r5 = VersionConvertor_10_50.convertResource(r2); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case DSTU2016May: + org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); + r5 = VersionConvertor_14_50.convertResource(r2a); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case STU3: + org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); + r5 = VersionConvertor_30_50.convertResource(r3, false); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case R4: + org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); + r5 = VersionConvertor_40_50.convertResource(r4); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + case R5: + r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); + if (r5 instanceof Questionnaire) + return (Questionnaire) r5; + else + return null; + } } + } + return null; + } catch (IOException e) { + throw new FHIRException(e); + } + } + + private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + String text = element.getNamedChildValue("text"); + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), I18nConstants.QUESTIONNAIRE_QR_ITEM_TEXT, qItem.getLinkId()); + + List answers = new ArrayList(); + element.getNamedChildren("answer", answers); + if (inProgress) + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), I18nConstants.QUESTIONNAIRE_QR_ITEM_MISSING, qItem.getLinkId()); + else if (myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe)) { + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), I18nConstants.QUESTIONNAIRE_QR_ITEM_MISSING, qItem.getLinkId()); + } else if (!answers.isEmpty()) { // items without answers should be allowed, but not items with answers to questions that are disabled + // it appears that this is always a duplicate error - it will always already have beeb reported, so no need to report it again? + // GDG 2019-07-13 +// rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), !isAnswerRequirementFulfilled(qItem, answers), I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTENABLED, qItem.getLinkId()); } - private void checkAllInterlinked(List errors, List entries, NodeStack stack, Element bundle, boolean isError) { - List entryList = new ArrayList<>(); - for (Element entry : entries) { - Element r = entry.getNamedChild("resource"); - if (r != null) { - entryList.add(new EntrySummary(entry, r)); - } - } - for (EntrySummary e : entryList) { - Set references = findReferences(e.getEntry()); - for (String ref : references) { - Element tgt = resolveInBundle(entries, ref, e.getEntry().getChildValue("fullUrl"), e.getResource().fhirType(), e.getResource().getIdBase()); - if (tgt != null) { - EntrySummary t = entryForTarget(entryList, tgt); - if (t != null) { - e.getTargets().add(t); - } - } - } - } + if (answers.size() > 1) + rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEA); - Set visited = new HashSet<>(); - visitLinked(visited, entryList.get(0)); - boolean foundRevLinks; - do { - foundRevLinks = false; - for (EntrySummary e : entryList) { - if (!visited.contains(e)) { - boolean add = false; - for (EntrySummary t : e.getTargets()) { - if (visited.contains(t)) { - add = true; - } - } - if (add) { - foundRevLinks = true; - visitLinked(visited, e); - } - } + for (Element answer : answers) { + NodeStack ns = stack.push(answer, -1, null, null); + if (qItem.getType() != null) { + switch (qItem.getType()) { + case GROUP: + rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_GROUP); + break; + case DISPLAY: // nothing + break; + case BOOLEAN: + validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); + break; + case DECIMAL: + validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); + break; + case INTEGER: + validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); + break; + case DATE: + validateQuestionnaireResponseItemType(errors, answer, ns, "date"); + break; + case DATETIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); + break; + case TIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "time"); + break; + case STRING: + validateQuestionnaireResponseItemType(errors, answer, ns, "string"); + break; + case TEXT: + validateQuestionnaireResponseItemType(errors, answer, ns, "text"); + break; + case URL: + validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); + break; + case ATTACHMENT: + validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); + break; + case REFERENCE: + validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); + break; + case QUANTITY: + if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) + if (qItem.hasExtension("???")) + validateQuestionnaireResponseItemQuantity(errors, answer, ns); + break; + case CHOICE: + String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) + checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); } - } while (foundRevLinks); + break; + case OPENCHOICE: + itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) + checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) + checkOption(errors, answer, ns, qsrc, qItem, "string", true); + } + break; +// case QUESTION: + case NULL: + // no validation + break; + } + } + validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); + } + if (qItem.getType() == null) { + fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTYPE, qItem.getLinkId()); + } else if (qItem.getType() == QuestionnaireItemType.DISPLAY) { + List items = new ArrayList(); + element.getNamedChildren("item", items); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), I18nConstants.QUESTIONNAIRE_QR_ITEM_DISPLAY, qItem.getLinkId()); + } else { + validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot, qstack); + } + } + private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { + return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; + } + + private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + if (elements.size() > 1) + rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEI, qItem.getLinkId()); + int i = 0; + for (Element element : elements) { + NodeStack ns = stack.push(element, i, null, null); + validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); + i++; + } + } + + private int getLinkIdIndex(List qItems, String linkId) { + for (int i = 0; i < qItems.size(); i++) { + if (linkId.equals(qItems.get(i).getLinkId())) + return i; + } + return -1; + } + + private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + List items = new ArrayList(); + element.getNamedChildren("item", items); + // now, sort into stacks + Map> map = new HashMap>(); + int lastIndex = -1; + for (Element item : items) { + String linkId = item.getNamedChildValue("linkId"); + if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), I18nConstants.QUESTIONNAIRE_QR_ITEM_NOLINKID)) { + int index = getLinkIdIndex(qItems, linkId); + if (index == -1) { + QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); + if (qItem != null) { + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); + NodeStack ns = stack.push(item, -1, null, null); + validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); + } else + rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTFOUND, linkId); + } else { + rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, I18nConstants.QUESTIONNAIRE_QR_ITEM_ORDER); + lastIndex = index; + + // If an item has a child called "linkId" but no child called "answer", + // we'll treat it as not existing for the purposes of enableWhen validation + if (item.hasChildren("answer") || item.hasChildren("item")) { + List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); + mapItem.add(item); + } + } + } + } + + // ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order + for (QuestionnaireItemComponent qItem : qItems) { + List mapItem = map.get(qItem.getLinkId()); + validateQuestionnaireResponseItem(hostContext, qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); + } + } + + public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { + boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe); + if (mapItem != null) { + if (!enabled) { int i = 0; - for (EntrySummary e : entryList) { - Element entry = e.getEntry(); - if (isError) { - rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); - } else { - warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), "Entry " + (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "") + " isn't reachable by traversing from first Bundle entry"); - } - i++; + for (Element e : mapItem) { + NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition()); + rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTENABLED2, qItem.getLinkId()); + i++; } + } + + // Recursively validate child items + validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); + + } else { + + // item is missing, is the question enabled? + if (enabled && qItem.getRequired()) { + String message = "No response found for required item with id = '" + qItem.getLinkId() + "'"; + if (inProgress) { + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } else { + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } + } + } - private EntrySummary entryForTarget(List entryList, Element tgt) { - for (EntrySummary e : entryList) { - if (e.getEntry() == tgt) { - return e; - } + } + + private String misplacedItemError(QuestionnaireItemComponent qItem) { + return qItem.hasLinkId() ? String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId()) : "Structural Error: item is in the wrong place"; + } + + private void validateQuestionnaireResponseItemQuantity(List errors, Element answer, NodeStack stack) { + + } + + private String validateQuestionnaireResponseItemType(List errors, Element element, NodeStack stack, String... types) { + List values = new ArrayList(); + element.getNamedChildrenWithWildcard("value[x]", values); + for (int i = 0; i < types.length; i++) { + if (types[i].equals("text")) { + types[i] = "string"; + } + } + if (values.size() > 0) { + NodeStack ns = stack.push(values.get(0), -1, null, null); + CommaSeparatedStringBuilder l = new CommaSeparatedStringBuilder(); + for (String s : types) { + l.append(s); + if (values.get(0).getName().equals("value" + Utilities.capitalize(s))) + return (s); + } + if (types.length == 1) + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_WRONGTYPE, types[0]); + else + rule(errors, IssueType.STRUCTURE, values.get(0).line(), values.get(0).col(), ns.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_WRONGTYPE2, l.toString()); + } + return null; + } + + private QuestionnaireItemComponent findQuestionnaireItem(Questionnaire qSrc, String linkId) { + return findItem(qSrc.getItem(), linkId); + } + + private QuestionnaireItemComponent findItem(List list, String linkId) { + for (QuestionnaireItemComponent item : list) { + if (linkId.equals(item.getLinkId())) + return item; + QuestionnaireItemComponent result = findItem(item.getItem(), linkId); + if (result != null) + return result; + } + return null; + } + + private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { + ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); + if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(ref))) { + try { + Coding c = ObjectConverter.readAsCoding(value); + if (isBlank(c.getCode()) && isBlank(c.getSystem()) && isNotBlank(c.getDisplay())) { + if (theOpenChoice) { + return; + } } + + long t = System.nanoTime(); + ValidationResult res = context.validateCode(new ValidationOptions(stack.workingLang), c, vs); + txTime = txTime + (System.nanoTime() - t); + if (!res.isOk()) { + txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_BADOPTION, c.getSystem(), c.getCode()); + } else if (res.getSeverity() != null) { + super.addValidationMessage(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), res.getMessage(), res.getSeverity(), Source.TerminologyEngine); + } + } catch (Exception e) { + warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_CODING, e.getMessage()); + } + } + } + + private void validateAnswerCode(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) { + Element v = answer.getNamedChild("valueCoding"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) + checkCodingOption(errors, answer, stack, qSrc, qItem, theOpenChoice); + // validateAnswerCode(errors, v, stack, qItem.getOption()); + else if (qItem.hasAnswerValueSet()) + validateAnswerCode(errors, v, stack, qSrc, qItem.getAnswerValueSet(), theOpenChoice); + else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONS); + } + + private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { + checkOption(errors, answer, stack, qSrc, qItem, type, false); + } + + private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { + if (type.equals("integer")) checkIntegerOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("date")) checkDateOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("time")) checkTimeOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("string")) checkStringOption(errors, answer, stack, qSrc, qItem, openChoice); + else if (type.equals("Coding")) checkCodingOption(errors, answer, stack, qSrc, qItem, openChoice); + } + + private void checkIntegerOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueInteger"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueIntegerType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSINTEGER); + } else { + boolean found = false; + for (IntegerType item : list) { + if (item.getValue() == Integer.parseInt(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOINTEGER, v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_INTNOOPTIONS); + } + + private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueDate"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueDateType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSDATE); + } else { + boolean found = false; + for (DateType item : list) { + if (item.getValue().equals(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NODATE, v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_DATENOOPTIONS); + } + + private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueTime"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + list.add(components.getValueTimeType()); + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSTIME); + } else { + boolean found = false; + for (TimeType item : list) { + if (item.getValue().equals(v.primitiveValue())) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTIME, v.primitiveValue()); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS); + } + + private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueString"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + if (components.getValue() != null) { + list.add(components.getValueStringType()); + } + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (!openChoice) { + if (list.isEmpty()) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING); + } else { + boolean found = false; + for (StringType item : list) { + if (item.getValue().equals((v.primitiveValue()))) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOSTRING, v.primitiveValue()); + } + } + } + } else { + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS); + } + } + + private void checkCodingOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + Element v = answer.getNamedChild("valueCoding"); + String system = v.getNamedChildValue("system"); + String code = v.getNamedChildValue("code"); + NodeStack ns = stack.push(v, -1, null, null); + if (qItem.getAnswerOption().size() > 0) { + List list = new ArrayList(); + for (QuestionnaireItemAnswerOptionComponent components : qItem.getAnswerOption()) { + try { + if (components.getValue() != null) { + list.add(components.getValueCoding()); + } + } catch (FHIRException e) { + // If it's the wrong type, just keep going + } + } + if (list.isEmpty() && !openChoice) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONSCODING); + } else { + boolean found = false; + for (Coding item : list) { + if (ObjectUtil.equals(item.getSystem(), system) && ObjectUtil.equals(item.getCode(), code)) { + found = true; + break; + } + } + if (!found) { + rule(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), found, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOCODING, system, code); + } + } + } else + hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_CODINGNOOPTIONS); + } + + private String tail(String path) { + return path.substring(path.lastIndexOf(".") + 1); + } + + private String tryParse(String ref) { + String[] parts = ref.split("\\/"); + switch (parts.length) { + case 1: return null; + case 2: + return checkResourceType(parts[0]); + default: + if (parts[parts.length - 2].equals("_history")) + return checkResourceType(parts[parts.length - 4]); + else + return checkResourceType(parts[parts.length - 2]); } + } - private void visitLinked(Set visited, EntrySummary t) { - if (!visited.contains(t)) { - visited.add(t); - for (EntrySummary e : t.getTargets()) { - visitLinked(visited, e); - } + private boolean typesAreAllReference(List theType) { + for (TypeRefComponent typeRefComponent : theType) { + if (typeRefComponent.getCode().equals("Reference") == false) { + return false; + } + } + return true; + } + + private void validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials) { + List entries = new ArrayList(); + bundle.getNamedChildren("entry", entries); + String type = bundle.getNamedChildValue("type"); + type = StringUtils.defaultString(type); + + if (entries.size() == 0) { + rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRST); + } else { + // Get the first entry, the MessageHeader + Element firstEntry = entries.get(0); + // Get the stack of the first entry + NodeStack firstStack = stack.push(firstEntry, 1, null, null); + + String fullUrl = firstEntry.getNamedChildValue("fullUrl"); + + if (type.equals("document")) { + Element resource = firstEntry.getNamedChild("resource"); + String id = resource.getNamedChildValue("id"); + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) { + validateDocument(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } - } - - private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack) { - followResourceLinks(entry, visitedResources, candidateEntries, candidateResources, errors, stack, 0); - } - - private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack, int depth) { - Element resource = entry.getNamedChild("resource"); - if (visitedResources.containsValue(resource)) - return; - - visitedResources.put(entry.getNamedChildValue("fullUrl"), resource); - - String type = null; - Set references = findReferences(resource); - for (String reference : references) { - // We don't want errors when just retrieving the element as they will be caught (with better path info) in subsequent processing - IndexedElement r = getFromBundle(stack.getElement(), reference, entry.getChildValue("fullUrl"), new ArrayList(), stack.addToLiteralPath("entry[" + candidateResources.indexOf(resource) + "]"), type, "transaction".equals(stack.getElement().getChildValue("type"))); - if (r != null && !visitedResources.containsValue(r.getMatch())) { - followResourceLinks(candidateEntries.get(r.getMatch()), visitedResources, candidateEntries, candidateResources, errors, stack, depth + 1); - } + checkAllInterlinked(errors, entries, stack, bundle, true); + } + if (type.equals("message")) { + Element resource = firstEntry.getNamedChild("resource"); + String id = resource.getNamedChildValue("id"); + if (rule(errors, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath("entry", ":0"), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) { + validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id); } + checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())); + } + // We do not yet have rules requiring that the id and fullUrl match when dealing with messaging Bundles + // validateResourceIds(errors, entries, stack); + } + for (Element entry : entries) { + String fullUrl = entry.getNamedChildValue("fullUrl"); + String url = getCanonicalURLForEntry(entry); + String id = getIdForEntry(entry); + if (url != null) { + if (!(!url.equals(fullUrl) || (url.matches(uriRegexForVersion()) && url.endsWith("/" + id))) && !isV3orV2Url(url)) + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MISMATCHIDURL, url, fullUrl, id); + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry", ":0"), !url.equals(fullUrl) || serverBase == null || (url.equals(Utilities.pathURL(serverBase, entry.getNamedChild("resource").fhirType(), id))), I18nConstants.BUNDLE_BUNDLE_ENTRY_CANONICAL, url, fullUrl); + } + // todo: check specials + } + } + + // hack for pre-UTG v2/v3 + private boolean isV3orV2Url(String url) { + return url.startsWith("http://hl7.org/fhir/v3/") || url.startsWith("http://hl7.org/fhir/v2/"); + } + + public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; + private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; + private static final String EXECUTION_ID = "validator.execution.id"; + + private String uriRegexForVersion() { + if (VersionUtilities.isR3Ver(context.getVersion())) + return URI_REGEX3; + else + return Constants.URI_REGEX; + } + + private String getCanonicalURLForEntry(Element entry) { + Element e = entry.getNamedChild("resource"); + if (e == null) + return null; + return e.getNamedChildValue("url"); + } + + private String getIdForEntry(Element entry) { + Element e = entry.getNamedChild("resource"); + if (e == null) + return null; + return e.getNamedChildValue("id"); + } + + /** + * Check each resource entry to ensure that the entry's fullURL includes the resource's id + * value. Adds an ERROR ValidationMessge to errors List for a given entry if it references + * a resource and fullURL does not include the resource's id. + * + * @param errors List of ValidationMessage objects that new errors will be added to. + * @param entries List of entry Element objects to be checked. + * @param stack Current NodeStack used to create path names in error detail messages. + */ + private void validateResourceIds(List errors, List entries, NodeStack stack) { + // TODO: Need to handle _version + int i = 1; + for (Element entry : entries) { + String fullUrl = entry.getNamedChildValue("fullUrl"); + Element resource = entry.getNamedChild("resource"); + String id = resource != null ? resource.getNamedChildValue("id") : null; + if (id != null && fullUrl != null) { + String urlId = null; + if (fullUrl.startsWith("https://") || fullUrl.startsWith("http://")) { + urlId = fullUrl.substring(fullUrl.lastIndexOf('/') + 1); + } else if (fullUrl.startsWith("urn:uuid") || fullUrl.startsWith("urn:oid")) { + urlId = fullUrl.substring(fullUrl.lastIndexOf(':') + 1); + } + rule(errors, IssueType.INVALID, entry.line(), entry.col(), stack.addToLiteralPath("entry[" + i + "]"), urlId.equals(id), I18nConstants.BUNDLE_BUNDLE_ENTRY_IDURLMISMATCH, id, fullUrl); + } + i++; + } + } + + private void checkAllInterlinked(List errors, List entries, NodeStack stack, Element bundle, boolean isError) { + List entryList = new ArrayList<>(); + for (Element entry : entries) { + Element r = entry.getNamedChild("resource"); + if (r != null) { + entryList.add(new EntrySummary(entry, r)); + } + } + for (EntrySummary e : entryList) { + Set references = findReferences(e.getEntry()); + for (String ref : references) { + Element tgt = resolveInBundle(entries, ref, e.getEntry().getChildValue("fullUrl"), e.getResource().fhirType(), e.getResource().getIdBase()); + if (tgt != null) { + EntrySummary t = entryForTarget(entryList, tgt); + if (t != null) { + e.getTargets().add(t); + } + } + } } - private Set findReferences(Element start) { - Set references = new HashSet(); - findReferences(start, references); - return references; + Set visited = new HashSet<>(); + visitLinked(visited, entryList.get(0)); + boolean foundRevLinks; + do { + foundRevLinks = false; + for (EntrySummary e : entryList) { + if (!visited.contains(e)) { + boolean add = false; + for (EntrySummary t : e.getTargets()) { + if (visited.contains(t)) { + add = true; + } + } + if (add) { + foundRevLinks = true; + visitLinked(visited, e); + } + } + } + } while (foundRevLinks); + + int i = 0; + for (EntrySummary e : entryList) { + Element entry = e.getEntry(); + if (isError) { + rule(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN, (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + } else { + warning(errors, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath("entry" + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN, (entry.getChildValue("fullUrl") != null ? "'" + entry.getChildValue("fullUrl") + "'" : "")); + } + i++; + } + } + + private EntrySummary entryForTarget(List entryList, Element tgt) { + for (EntrySummary e : entryList) { + if (e.getEntry() == tgt) { + return e; + } + } + return null; + } + + private void visitLinked(Set visited, EntrySummary t) { + if (!visited.contains(t)) { + visited.add(t); + for (EntrySummary e : t.getTargets()) { + visitLinked(visited, e); + } + } + } + + private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack) { + followResourceLinks(entry, visitedResources, candidateEntries, candidateResources, errors, stack, 0); + } + + private void followResourceLinks(Element entry, Map visitedResources, Map candidateEntries, List candidateResources, List errors, NodeStack stack, int depth) { + Element resource = entry.getNamedChild("resource"); + if (visitedResources.containsValue(resource)) + return; + + visitedResources.put(entry.getNamedChildValue("fullUrl"), resource); + + String type = null; + Set references = findReferences(resource); + for (String reference : references) { + // We don't want errors when just retrieving the element as they will be caught (with better path info) in subsequent processing + IndexedElement r = getFromBundle(stack.getElement(), reference, entry.getChildValue("fullUrl"), new ArrayList(), stack.addToLiteralPath("entry[" + candidateResources.indexOf(resource) + "]"), type, "transaction".equals(stack.getElement().getChildValue("type"))); + if (r != null && !visitedResources.containsValue(r.getMatch())) { + followResourceLinks(candidateEntries.get(r.getMatch()), visitedResources, candidateEntries, candidateResources, errors, stack, depth + 1); + } + } + } + + private Set findReferences(Element start) { + Set references = new HashSet(); + findReferences(start, references); + return references; + } + + private void findReferences(Element start, Set references) { + for (Element child : start.getChildren()) { + if (child.getType().equals("Reference")) { + String ref = child.getChildValue("reference"); + if (ref != null && !ref.startsWith("#")) + references.add(ref); + } + if (child.getType().equals("url") || child.getType().equals("uri") || child.getType().equals("canonical")) { + String ref = child.primitiveValue(); + if (ref != null && !ref.startsWith("#")) + references.add(ref); + } + findReferences(child, references); + } + } + + private void validateBundleReference(List errors, List entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) { + String reference = null; + try { + reference = ref.getNamedChildValue("reference"); + } catch (Error e) { + } - private void findReferences(Element start, Set references) { - for (Element child : start.getChildren()) { - if (child.getType().equals("Reference")) { - String ref = child.getChildValue("reference"); - if (ref != null && !ref.startsWith("#")) - references.add(ref); - } - if (child.getType().equals("url") || child.getType().equals("uri") || child.getType().equals("canonical")) { - String ref = child.primitiveValue(); - if (ref != null && !ref.startsWith("#")) - references.add(ref); - } - findReferences(child, references); + if (ref != null && !Utilities.noString(reference)) { + Element target = resolveInBundle(entries, reference, fullUrl, type, id); + rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOTFOUND, reference, name); + } + } + + private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { + String resourceName = element.getType(); + TypeRefComponent trr = null; + for (TypeRefComponent tr : child.getType()) { + if (tr.getCode().equals("Resource")) { + trr = tr; + break; + } + } + if (trr == null) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName); + } else if (isValidResourceType(resourceName, trr)) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); + sdTime = sdTime + (System.nanoTime() - t); + // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise + ValidatorHostContext hc = null; + if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) { + resource = element; + hc = hostContext.forEntry(element); + } else { + hc = hostContext.forContained(element); + } + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) { + validateResource(hc, errors, resource, element, profile, idstatus, stack); + } + } else { + List types = new ArrayList<>(); + for (UriType u : trr.getProfile()) { + StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); + if (sd != null && !types.contains(sd.getType())) { + types.add(sd.getType()); } + } + if (types.size() == 1) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE2, resourceName, types.get(0)); + } else { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); + } + } + } + + private boolean isValidResourceType(String type, TypeRefComponent def) { + if (!def.hasProfile()) { + return true; + } + List list = new ArrayList<>(); + for (UriType u : def.getProfile()) { + StructureDefinition sdt = context.fetchResource(StructureDefinition.class, u.getValue()); + if (sdt != null) { + list.add(sdt); + } } - private void validateBundleReference(List errors, List entries, Element ref, String name, NodeStack stack, String fullUrl, String type, String id) { - String reference = null; - try { - reference = ref.getNamedChildValue("reference"); - } catch (Error e) { - - } - - if (ref != null && !Utilities.noString(reference)) { - Element target = resolveInBundle(entries, reference, fullUrl, type, id); - rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, "Can't find '" + reference + "' in the bundle (" + name + ")"); - } - } - - private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { - String resourceName = element.getType(); - TypeRefComponent trr = null; - for (TypeRefComponent tr : child.getType()) { - if (tr.getCode().equals("Resource")) { - trr = tr; - break; - } - } - if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + " is not valid - no resources allowed here"); - } else if (isValidResourceType(resourceName, trr)) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); - sdTime = sdTime + (System.nanoTime() - t); - // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise - ValidatorHostContext hc = null; - if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) { - resource = element; - hc = hostContext.forEntry(element); - } else { - hc = hostContext.forContained(element); - } - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'")) { - validateResource(hc, errors, resource, element, profile, idstatus, stack); - } - } else { - List types = new ArrayList<>(); - for (UriType u : trr.getProfile()) { - StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); - if (sd != null && !types.contains(sd.getType())) { - types.add(sd.getType()); - } - } - if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be " + types.get(0)); - } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '" + resourceName + "' is not valid - must be one of " + types); - } - } - } - - private boolean isValidResourceType(String type, TypeRefComponent def) { - if (!def.hasProfile()) { + StructureDefinition sdt = context.fetchTypeDefinition(type); + while (sdt != null) { + if (def.getWorkingCode().equals("Resource")) { + for (StructureDefinition sd : list) { + if (sd.getUrl().equals(sdt.getUrl())) { return true; + } + if (sd.getType().equals(sdt.getType())) { + return true; + } } - List list = new ArrayList<>(); - for (UriType u : def.getProfile()) { - StructureDefinition sdt = context.fetchResource(StructureDefinition.class, u.getValue()); - if (sdt != null) { - list.add(sdt); - } - } + } + sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition()); + } + return false; + } - StructureDefinition sdt = context.fetchTypeDefinition(type); - while (sdt != null) { - if (def.getWorkingCode().equals("Resource")) { - for (StructureDefinition sd : list) { - if (sd.getUrl().equals(sdt.getUrl())) { - return true; - } - if (sd.getType().equals(sdt.getType())) { - return true; - } - } - } - sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition()); - } - return false; + private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { + // first entry must be a composition + if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), I18nConstants.BUNDLE_BUNDLE_ENTRY_DOCUMENT)) { + + // the composition subject etc references must resolve in the bundle + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, true, "author", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "encounter", "Composition"); + validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "custodian", "Composition"); + validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party"); + validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail"); + + validateSections(errors, entries, composition, stack, fullUrl, id); + } + } + + public void validateDocumentSubReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) { + List list = new ArrayList<>(); + composition.getNamedChildren(parent, list); + int i = 1; + for (Element elem : list) { + validateDocumentReference(errors, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent); + i++; + } + } + + public void validateDocumentReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) { + if (repeats) { + List list = new ArrayList<>(); + composition.getNamedChildren(propName, list); + int i = 1; + for (Element elem : list) { + validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id); + i++; + } + + } else { + Element elem = composition.getNamedChild(propName); + if (elem != null) { + validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id); + } + } + } + + 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, boolean checkDisplayInContext, String extensionUrl) throws FHIRException { + + // check type invariants + checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); + if (definition.getFixed() != null) + checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); + + // get the list of direct defined children, including slices + List childDefinitions = ProfileUtilities.getChildMap(profile, definition); + if (childDefinitions.isEmpty()) { + if (actualType == null) + return; // there'll be an error elsewhere in this case, and we're going to stop. + childDefinitions = getActualTypeChildren(hostContext, element, actualType); + } else if (definition.getType().size() > 1) { + // this only happens when the profile constrains the abstract children but leaves th choice open. + if (actualType == null) + return; // there'll be an error elsewhere in this case, and we're going to stop. + List typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); + // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) + mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); } - private void validateDocument(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id) { - // first entry must be a composition - if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), - "The first entry in a document must be a composition")) { + List children = listChildren(element, stack); + List problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children); - // the composition subject etc references must resolve in the bundle - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "subject", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, true, "author", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "encounter", "Composition"); - validateDocumentReference(errors, entries, composition, stack, fullUrl, id, false, "custodian", "Composition"); - validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "attester", false, "party"); - validateDocumentSubReference(errors, entries, composition, stack, fullUrl, id, "Composition", "event", true, "detail"); + checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths); + // 4. check order if any slices are ordered. (todo) - validateSections(errors, entries, composition, stack, fullUrl, id); + // 5. inspect each child for validity + for (ElementInfo ei : children) { + checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl); + } + } + + private void mergeChildLists(List master, List additional, String masterPath, String typePath) { + for (ElementDefinition ed : additional) { + boolean inMaster = false; + for (ElementDefinition t : master) { + String tp = masterPath + ed.getPath().substring(typePath.length()); + if (t.getPath().equals(tp)) { + inMaster = true; } + } + if (!inMaster) { + master.add(ed); + } } - public void validateDocumentSubReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, String title, String parent, boolean repeats, String propName) { - List list = new ArrayList<>(); - composition.getNamedChildren(parent, list); - int i = 1; - for (Element elem : list) { - validateDocumentReference(errors, entries, elem, stack.push(elem, i, null, null), fullUrl, id, repeats, propName, title + "." + parent); - i++; + + } + + // todo: the element definition in context might assign a constrained profile for the type? + public List getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { + List childDefinitions; + StructureDefinition dt = null; + if (isAbsolute(actualType)) + dt = this.context.fetchResource(StructureDefinition.class, actualType); + else + dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); + if (dt == null) + throw new DefinitionException("Unable to resolve actual type " + actualType); + trackUsage(dt, hostContext, element); + + childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); + return childDefinitions; + } + + public void checkChild(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, + Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) + throws FHIRException, DefinitionException { + + List profiles = new ArrayList(); + if (ei.definition != null) { + String type = null; + ElementDefinition typeDefn = null; + checkMustSupport(profile, ei); + + if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getWorkingCode()) && !"Element".equals(ei.definition.getType().get(0).getWorkingCode()) + && !"BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode())) { + type = ei.definition.getType().get(0).getWorkingCode(); + // Excluding reference is a kludge to get around versioning issues + if (ei.definition.getType().get(0).hasProfile()) { + for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { + profiles.add(p.getValue()); + } } - } - - public void validateDocumentReference(List errors, List entries, Element composition, NodeStack stack, String fullUrl, String id, boolean repeats, String propName, String title) { - if (repeats) { - List list = new ArrayList<>(); - composition.getNamedChildren(propName, list); - int i = 1; - for (Element elem : list) { - validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, i, null, null), fullUrl, "Composition", id); - i++; - } - - } else { - Element elem = composition.getNamedChild(propName); - if (elem != null) { - validateBundleReference(errors, entries, elem, title + "." + propName, stack.push(elem, -1, null, null), fullUrl, "Composition", id); - } + } else if (ei.definition.getType().size() == 1 && "*".equals(ei.definition.getType().get(0).getWorkingCode())) { + String prefix = tail(ei.definition.getPath()); + assert prefix.endsWith("[x]"); + type = ei.getName().substring(prefix.length() - 3); + if (isPrimitiveType(type)) + type = Utilities.uncapitalize(type); + if (ei.definition.getType().get(0).hasProfile()) { + for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { + profiles.add(p.getValue()); + } } - } + } else if (ei.definition.getType().size() > 1) { - 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, boolean checkDisplayInContext, String extensionUrl) throws FHIRException { + String prefix = tail(ei.definition.getPath()); + assert typesAreAllReference(ei.definition.getType()) || ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; - // check type invariants - checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); - if (definition.getFixed() != null) - checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); - - // get the list of direct defined children, including slices - List childDefinitions = ProfileUtilities.getChildMap(profile, definition); - if (childDefinitions.isEmpty()) { - if (actualType == null) - return; // there'll be an error elsewhere in this case, and we're going to stop. - childDefinitions = getActualTypeChildren(hostContext, element, actualType); - } else if (definition.getType().size() > 1) { - // this only happens when the profile constrains the abstract children but leaves th choice open. - if (actualType == null) - return; // there'll be an error elsewhere in this case, and we're going to stop. - List typeChildDefinitions = getActualTypeChildren(hostContext, element, actualType); - // what were going to do is merge them - the type is not allowed to constrain things that the child definitions already do (well, if it does, it'll be ignored) - mergeChildLists(childDefinitions, typeChildDefinitions, definition.getPath(), actualType); - } - - List children = listChildren(element, stack); - List problematicPaths = assignChildren(hostContext, errors, profile, resource, stack, childDefinitions, children); - - checkCardinalities(errors, profile, element, stack, childDefinitions, children, problematicPaths); - // 4. check order if any slices are ordered. (todo) - - // 5. inspect each child for validity - for (ElementInfo ei : children) { - checkChild(hostContext, errors, profile, definition, resource, element, actualType, stack, inCodeableConcept, checkDisplayInContext, ei, extensionUrl); - } - } - - private void mergeChildLists(List master, List additional, String masterPath, String typePath) { - for (ElementDefinition ed : additional) { - boolean inMaster = false; - for (ElementDefinition t : master) { - String tp = masterPath + ed.getPath().substring(typePath.length()); - if (t.getPath().equals(tp)) { - inMaster = true; - } - } - if (!inMaster) { - master.add(ed); - } - } - - - } - - // todo: the element definition in context might assign a constrained profile for the type? - public List getActualTypeChildren(ValidatorHostContext hostContext, Element element, String actualType) { - List childDefinitions; - StructureDefinition dt = null; - if (isAbsolute(actualType)) - dt = this.context.fetchResource(StructureDefinition.class, actualType); - else - dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); - if (dt == null) - throw new DefinitionException("Unable to resolve actual type " + actualType); - trackUsage(dt, hostContext, element); - - childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); - return childDefinitions; - } - - public void checkChild(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, - Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) - throws FHIRException, DefinitionException { - - List profiles = new ArrayList(); - if (ei.definition != null) { - String type = null; - ElementDefinition typeDefn = null; - checkMustSupport(profile, ei); - - if (ei.definition.getType().size() == 1 && !"*".equals(ei.definition.getType().get(0).getWorkingCode()) && !"Element".equals(ei.definition.getType().get(0).getWorkingCode()) - && !"BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode())) { - type = ei.definition.getType().get(0).getWorkingCode(); - // Excluding reference is a kludge to get around versioning issues - if (ei.definition.getType().get(0).hasProfile()) { - for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { - profiles.add(p.getValue()); - } - } - } else if (ei.definition.getType().size() == 1 && "*".equals(ei.definition.getType().get(0).getWorkingCode())) { - String prefix = tail(ei.definition.getPath()); - assert prefix.endsWith("[x]"); - type = ei.getName().substring(prefix.length() - 3); - if (isPrimitiveType(type)) - type = Utilities.uncapitalize(type); - if (ei.definition.getType().get(0).hasProfile()) { - for (CanonicalType p : ei.definition.getType().get(0).getProfile()) { - profiles.add(p.getValue()); - } - } - } else if (ei.definition.getType().size() > 1) { - - String prefix = tail(ei.definition.getPath()); - assert typesAreAllReference(ei.definition.getType()) || ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") : prefix; - - if (ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR)) - type = ei.getElement().getType(); - else { - prefix = prefix.substring(0, prefix.length() - 3); - for (TypeRefComponent t : ei.definition.getType()) - if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { - type = t.getWorkingCode(); - // Excluding reference is a kludge to get around versioning issues - if (t.hasProfile() && !type.equals("Reference")) - profiles.add(t.getProfile().get(0).getValue()); - } - } - if (type == null) { - TypeRefComponent trc = ei.definition.getType().get(0); - if (trc.getWorkingCode().equals("Reference")) - type = "Reference"; - else - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, - "The type of element " + ei.getName() + " is not known, which is illegal. Valid types at this point are " + describeTypes(ei.definition.getType())); - } - } else if (ei.definition.getContentReference() != null) { - typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); - } else if (ei.definition.getType().size() == 1 && ("Element".equals(ei.definition.getType().get(0).getWorkingCode()) || "BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode()))) { - if (ei.definition.getType().get(0).hasProfile()) { - CanonicalType pu = ei.definition.getType().get(0).getProfile().get(0); - if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) - profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT)); - else - profiles.add(pu.getValue()); - } - } - - if (type != null) { - if (type.startsWith("@")) { - ei.definition = findElement(profile, type.substring(1)); - type = null; - } - } - NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); - if (debug) { - System.out.println(" " + localStack.getLiteralPath()); - } - String localStackLiterapPath = localStack.getLiteralPath(); - String eiPath = ei.getPath(); - assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; - boolean thisIsCodeableConcept = false; - String thisExtension = null; - boolean checkDisplay = true; - - checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.getElement(), localStack, true); - - ei.getElement().markValidation(profile, ei.definition); - boolean elementValidated = false; - if (type != null) { - if (isPrimitiveType(type)) { - checkPrimitive(hostContext, errors, ei.getPath(), type, ei.definition, ei.getElement(), profile, stack); - } else { - if (ei.definition.hasFixed()) { - checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getFixed(), profile.getUrl(), ei.definition.getSliceName(), null); - } - if (ei.definition.hasPattern()) { - checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getPattern(), profile.getUrl(), ei.definition.getSliceName(), null, true); - } - } - if (type.equals("Identifier")) { - checkIdentifier(errors, ei.getPath(), ei.getElement(), ei.definition); - } else if (type.equals("Coding")) { - checkCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack); - } else if (type.equals("CodeableConcept")) { - checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack); - thisIsCodeableConcept = true; - } else if (type.equals("Reference")) { - checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, ei.definition, actualType, localStack); - // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension - } else if (type.equals("Extension")) { - Element eurl = ei.getElement().getNamedChild("url"); - if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, "Extension.url is required")) { - String url = eurl.primitiveValue(); - thisExtension = url; - if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), "Extension.url is required")) { - if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), "Extension.url must be an absolute URL")) { - checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); - } - } - } - } else if (type.equals("Resource")) { - validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if - elementValidated = true; - // (str.matches(".*([.,/])work\\1$")) - } else if (Utilities.isAbsoluteUrl(type)) { - StructureDefinition defn = context.fetchTypeDefinition(type); - if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) { - List txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep()); - if (txtype.contains("CodeableConcept")) { - checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack, defn); - thisIsCodeableConcept = true; - } else if (txtype.contains("Coding")) { - checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn); - } - } - } - } else { - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, "Unrecognised Content " + ei.getName())) - validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); - } - StructureDefinition p = null; - String tail = null; - if (profiles.isEmpty()) { - if (type != null) { - p = getProfileForType(type, ei.definition.getType()); - - // If dealing with a primitive type, then we need to check the current child against - // the invariants (constraints) on the current element, because otherwise it only gets - // checked against the primary type's invariants: LLoyd - //if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { - // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); - //} - - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown type " + type); - } - } else if (profiles.size() == 1) { - String url = profiles.get(0); - if (url.contains("#")) { - tail = url.substring(url.indexOf("#") + 1); - url = url.substring(0, url.indexOf("#")); - } - p = this.context.fetchResource(StructureDefinition.class, url); - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + profiles.get(0)); - } else { - elementValidated = true; - HashMap> goodProfiles = new HashMap>(); - HashMap> badProfiles = new HashMap>(); - for (String typeProfile : profiles) { - String url = typeProfile; - tail = null; - if (url.contains("#")) { - tail = url.substring(url.indexOf("#") + 1); - url = url.substring(0, url.indexOf("#")); - } - p = this.context.fetchResource(StructureDefinition.class, typeProfile); - if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, "Unknown profile " + typeProfile)) { - List profileErrors = new ArrayList(); - validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - if (hasErrors(profileErrors)) - badProfiles.put(typeProfile, profileErrors); - else - goodProfiles.put(typeProfile, profileErrors); - } - } - if (goodProfiles.size() == 1) { - errors.addAll(goodProfiles.values().iterator().next()); - } else if (goodProfiles.size() == 0) { - rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Unable to find matching profile among choices: " + StringUtils.join("; ", profiles)); - for (String m : badProfiles.keySet()) { - p = this.context.fetchResource(StructureDefinition.class, m); - for (ValidationMessage message : badProfiles.get(m)) { - message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); - errors.add(message); - } - } - } else { - warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, "Found multiple matching profiles among choices: " + StringUtils.join("; ", goodProfiles.keySet())); - for (String m : goodProfiles.keySet()) { - p = this.context.fetchResource(StructureDefinition.class, m); - for (ValidationMessage message : goodProfiles.get(m)) { - message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); - errors.add(message); - } - } - } - } - if (p != null) { - trackUsage(p, hostContext, element); - - if (!elementValidated) { - if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - else - validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - } - 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.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); - } - } - } - } - - private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { - if (tracker != null) { - tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); - } - } - - private boolean hasMapping(String url, StructureDefinition defn, ElementDefinition elem) { - String id = null; - for (StructureDefinitionMappingComponent m : defn.getMapping()) { - if (url.equals(m.getUri())) { - id = m.getIdentity(); - break; - } - } - if (id != null) { - for (ElementDefinitionMappingComponent m : elem.getMapping()) { - if (id.equals(m.getIdentity())) { - return true; - } - } - - } - return false; - } - - private List getMapping(String url, StructureDefinition defn, ElementDefinition elem) { - List res = new ArrayList<>(); - String id = null; - for (StructureDefinitionMappingComponent m : defn.getMapping()) { - if (url.equals(m.getUri())) { - id = m.getIdentity(); - break; - } - } - if (id != null) { - for (ElementDefinitionMappingComponent m : elem.getMapping()) { - if (id.equals(m.getIdentity())) { - res.add(m.getMap()); - } - } - } - return res; - } - - public void checkMustSupport(StructureDefinition profile, ElementInfo ei) { - String usesMustSupport = profile.getUserString("usesMustSupport"); - if (usesMustSupport == null) { - usesMustSupport = "N"; - for (ElementDefinition pe : profile.getSnapshot().getElement()) { - if (pe.getMustSupport()) { - usesMustSupport = "Y"; - break; - } - } - profile.setUserData("usesMustSupport", usesMustSupport); - } - if (usesMustSupport.equals("Y")) { - String elementSupported = ei.getElement().getUserString("elementSupported"); - if (elementSupported == null || ei.definition.getMustSupport()) - if (ei.definition.getMustSupport()) { - ei.getElement().setUserData("elementSupported", "Y"); - } - } - } - - public void checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, - List childDefinitions, List children, List problematicPaths) throws DefinitionException { - // 3. report any definitions that have a cardinality problem - for (ElementDefinition ed : childDefinitions) { - if (ed.getRepresentation().isEmpty()) { // ignore xml attributes - int count = 0; - List slices = null; - if (ed.hasSlicing()) - slices = ProfileUtilities.getSliceList(profile, ed); - for (ElementInfo ei : children) - if (ei.definition == ed) - count++; - else if (slices != null) { - for (ElementDefinition sed : slices) { - if (ei.definition == sed) { - count++; - break; - } - } - } - String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; - if (ed.getMin() > 0) { - if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + "': Unable to check minimum required (" + Integer.toString(ed.getMin()) + ") due to lack of slicing validation"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), location + ": minimum required = " + Integer.toString(ed.getMin()) + ", but only found " + Integer.toString(count)); - } - if (ed.hasMax() && !ed.getMax().equals("*")) { - if (problematicPaths.contains(ed.getPath())) - hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": Unable to check max allowed (" + ed.getMax() + ") due to lack of slicing validation"); - else - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), location + ": max allowed = " + ed.getMax() + ", but found " + Integer.toString(count)); - } - } - } - } - - public List assignChildren(ValidatorHostContext hostContext, List errors, StructureDefinition profile, Element resource, - NodeStack stack, List childDefinitions, List children) throws DefinitionException { - // 2. assign children to a definition - // for each definition, for each child, check whether it belongs in the slice - ElementDefinition slicer = null; - boolean unsupportedSlicing = false; - List problematicPaths = new ArrayList(); - String slicingPath = null; - int sliceOffset = 0; - for (int i = 0; i < childDefinitions.size(); i++) { - ElementDefinition ed = childDefinitions.get(i); - boolean childUnsupportedSlicing = false; - boolean process = true; - if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) - slicingPath = ed.getPath(); - else if (slicingPath != null && ed.getPath().equals(slicingPath)) - ; // nothing - else if (slicingPath != null && !ed.getPath().startsWith(slicingPath)) - slicingPath = null; - // where are we with slicing - if (ed.hasSlicing()) { - if (slicer != null && slicer.getPath().equals(ed.getPath())) { - String errorContext = "profile " + profile.getUrl(); - if (!resource.getChildValue("id").isEmpty()) - errorContext += "; instance " + resource.getChildValue("id"); - throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); - } - slicer = ed; - process = false; - sliceOffset = i; - } else if (slicer != null && !slicer.getPath().equals(ed.getPath())) - slicer = null; - - for (ElementInfo ei : children) { - if (ei.sliceInfo == null) { - ei.sliceInfo = new ArrayList<>(); - } - unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei); - } - } - int last = -1; - int lastSlice = -1; - for (ElementInfo ei : children) { - String sliceInfo = ""; - if (slicer != null) - sliceInfo = " (slice: " + slicer.getPath() + ")"; - if (!unsupportedSlicing) - if (ei.additionalSlice && ei.definition != null) { - if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) || - ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) { - slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), - "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); - } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), - "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); - } - } else { - // Don't raise this if we're in an abstract profile, like Resource - if (!profile.getAbstract()) - rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), "This element is not allowed by the profile " + profile.getUrl()); - } - // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements - boolean isXmlAttr = false; - if (ei.definition != null) { - for (Enumeration r : ei.definition.getRepresentation()) { - if (r.getValue() == PropertyRepresentation.XMLATTR) { - isXmlAttr = true; - break; - } - } - } - - if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { - boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order"); - } - if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, "As specified by profile " + profile.getUrl() + ", Element '" + ei.getName() + "' is out of order in ordered slice"); - if (ei.definition == null || !isXmlAttr) - last = ei.index; - if (ei.slice != null) - lastSlice = ei.sliceindex; - else - lastSlice = -1; - } - return problematicPaths; - } - - public List listChildren(Element element, NodeStack stack) { - // 1. List the children, and remember their exact path (convenience) - List children = new ArrayList(); - ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element); - while (iter.next()) - children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count())); - return children; - } - - public void checkInvariants(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException { - checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); - } - - public boolean matchSlice(ValidatorHostContext hostContext, List errors, List sliceInfo, StructureDefinition profile, NodeStack stack, - ElementDefinition slicer, boolean unsupportedSlicing, List problematicPaths, int sliceOffset, int i, ElementDefinition ed, - boolean childUnsupportedSlicing, ElementInfo ei) { - boolean match = false; - if (slicer == null || slicer == ed) { - match = nameMatches(ei.getName(), tail(ed.getPath())); - } else { - if (nameMatches(ei.getName(), tail(ed.getPath()))) - try { - match = sliceMatches(hostContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack); - if (match) { - ei.slice = slicer; - - // Since a defined slice was found, this is not an additional (undefined) slice. - ei.additionalSlice = false; - } else if (ei.slice == null) { - // if the specified slice is undefined, keep track of the fact this is an additional (undefined) slice, but only if a slice wasn't found previously - ei.additionalSlice = true; - } - } catch (FHIRException e) { - rule(errors, IssueType.PROCESSING, ei.line(), ei.col(), ei.getPath(), false, e.getMessage()); - unsupportedSlicing = true; - childUnsupportedSlicing = true; - } - } - if (match) { - boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); - if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, "Profile " + profile.getUrl() + ", Element matches more than one slice - " + (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()) + ", " + (ed.hasSliceName() ? ed.getSliceName() : ""))) { - ei.definition = ed; - if (ei.slice == null) { - ei.index = i; - } else { - ei.index = sliceOffset; - ei.sliceindex = i - (sliceOffset + 1); - } - } - } else if (childUnsupportedSlicing) { - problematicPaths.add(ed.getPath()); - } - return unsupportedSlicing; - } - - private ElementDefinition getElementByTail(StructureDefinition p, String tail) throws DefinitionException { - if (tail == null) - return p.getSnapshot().getElement().get(0); - for (ElementDefinition t : p.getSnapshot().getElement()) { - if (tail.equals(t.getId())) - return t; - } - throw new DefinitionException("Unable to find element with id '" + tail + "'"); - } - - private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { - if (isBundleEntry(ei.getPath())) { - Element req = ep.getNamedChild("request"); - Element resp = ep.getNamedChild("response"); - Element fullUrl = ep.getNamedChild("fullUrl"); - Element method = null; - Element url = null; - if (req != null) { - method = req.getNamedChild("method"); - url = req.getNamedChild("url"); - } - if (resp != null) { - return IdStatus.OPTIONAL; - } - if (method == null) { - if (fullUrl == null) - return IdStatus.REQUIRED; - else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:")) - return IdStatus.OPTIONAL; - else - return IdStatus.REQUIRED; - } else { - String s = method.primitiveValue(); - if (s.equals("PUT")) { - if (url == null) - return IdStatus.REQUIRED; - else - return IdStatus.OPTIONAL; // or maybe prohibited? not clear - } else if (s.equals("POST")) - return IdStatus.OPTIONAL; // this should be prohibited, but see task 9102 - else // actually, we should never get to here; a bundle entry with method get/delete should not have a resource - return IdStatus.OPTIONAL; - } - } else if (isParametersEntry(ei.getPath()) || isBundleOutcome(ei.getPath())) - return IdStatus.OPTIONAL; - else - return IdStatus.REQUIRED; - } - - private void checkInvariants(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException { - if (noInvariantChecks) - return; - - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || profile.getUrl().equals(inv.getSource()))) { - @SuppressWarnings("unchecked") - Set invList = executionId.equals(element.getUserString(EXECUTION_ID)) ? (Set) element.getUserData(EXECUTED_CONSTRAINT_LIST) : null; - if (invList == null) { - invList = new HashSet<>(); - element.setUserData(EXECUTED_CONSTRAINT_LIST, invList); - element.setUserData(EXECUTION_ID, executionId); - } - if (!invList.contains(inv.getKey())) { - invList.add(inv.getKey()); - checkInvariant(hostContext, errors, path, profile, resource, element, inv); - } else { - //System.out.println("Skip "+inv.getKey()+" on "+path); - } - } - } - } - - public void checkInvariant(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { - ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); - if (n == null) { - long t = System.nanoTime(); - try { - n = fpe.parse(fixExpr(inv.getExpression())); - } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); - } - fpeTime = fpeTime + (System.nanoTime() - t); - inv.setUserData("validator.expression.cache", n); - } - - String msg; - boolean ok; - try { - long t = System.nanoTime(); - ok = fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), element, n); - fpeTime = fpeTime + (System.nanoTime() - t); - msg = fpe.forLog(); - } catch (Exception ex) { - ok = false; - msg = ex.getMessage(); - } - if (!ok) { - if (!Utilities.noString(msg)) - msg = " (" + msg + ")"; - 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() + "]"); - else if (bpWarnings == BestPracticeWarningLevel.Warning) - warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - else if (bpWarnings == BestPracticeWarningLevel.Error) - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { - rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { - warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); - } - } - } - - private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { - // first entry must be a messageheader - if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), - "The first entry in a message must be a MessageHeader")) { - List elements = messageHeader.getChildren("focus"); - for (Element elem : elements) - validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); - } - } - - private void validateObservation(List errors, Element element, NodeStack stack) { - // all observations should have a subject, a performer, and a time - - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); - List performers = new ArrayList<>(); - element.getNamedChildren("performer", performers); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, - "All observations should have an effectiveDateTime or an effectivePeriod"); - } - - /* - * The actual base entry point for internal use (re-entrant) - */ - private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { - assert stack != null; - assert resource != null; - boolean ok = true; - String resourceName = element.getType(); // todo: consider namespace...? - if (defn == null) { - long t = System.nanoTime(); - defn = element.getProperty().getStructure(); - if (defn == null) - defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); - sdTime = sdTime + (System.nanoTime() - t); - ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, "No definition found for resource type '" + resourceName + "'"); - } - - String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); - // special case: we have a bundle, and the profile is not for a bundle. We'll try the first entry instead - if (!type.equals(resourceName) && resourceName.equals("Bundle")) { - NodeStack first = getFirstEntry(stack); - if (first != null && first.getElement().getType().equals(type)) { - element = first.element; - stack = first; - resourceName = element.getType(); - idstatus = IdStatus.OPTIONAL; // why? - } - // todo: validate everything in this bundle. - } - ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), "Specified profile type was '" + type + "', but found type '" + resourceName + "'"); - - if (ok) { - if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource requires an id, but none is present"); - else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, "Resource has an id, but none is allowed"); - start(hostContext, errors, element, element, defn, stack); // root is both definition and type - } - } - - private NodeStack getFirstEntry(NodeStack bundle) { - List list = new ArrayList(); - bundle.getElement().getNamedChildren("entry", list); - if (list.isEmpty()) - return null; - Element resource = list.get(0).getNamedChild("resource"); - if (resource == null) - return null; + if (ei.definition.hasRepresentation(PropertyRepresentation.TYPEATTR)) + type = ei.getElement().getType(); else { - NodeStack entry = bundle.push(list.get(0), 0, list.get(0).getProperty().getDefinition(), list.get(0).getProperty().getDefinition()); - return entry.push(resource, -1, resource.getProperty().getDefinition(), context.fetchTypeDefinition(resource.fhirType()).getSnapshot().getElementFirstRep()); - } - } - - private void validateSections(List errors, List entries, Element focus, NodeStack stack, String fullUrl, String id) { - List sections = new ArrayList(); - focus.getNamedChildren("section", sections); - int i = 1; - for (Element section : sections) { - NodeStack localStack = stack.push(section, i, null, null); - - // technically R4+, but there won't be matches from before that - validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "author", "Section"); - validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "focus", "Section"); - - List sectionEntries = new ArrayList(); - section.getNamedChildren("entry", sectionEntries); - int j = 1; - for (Element sectionEntry : sectionEntries) { - NodeStack localStack2 = localStack.push(sectionEntry, j, null, null); - validateBundleReference(errors, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id); - j++; + prefix = prefix.substring(0, prefix.length() - 3); + for (TypeRefComponent t : ei.definition.getType()) + if ((prefix + Utilities.capitalize(t.getWorkingCode())).equals(ei.getName())) { + type = t.getWorkingCode(); + // Excluding reference is a kludge to get around versioning issues + if (t.hasProfile() && !type.equals("Reference")) + profiles.add(t.getProfile().get(0).getValue()); } - validateSections(errors, entries, section, localStack, fullUrl, id); - i++; } - } + if (type == null) { + TypeRefComponent trc = ei.definition.getType().get(0); + if (trc.getWorkingCode().equals("Reference")) + type = "Reference"; + else + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTYPE, ei.getName(), describeTypes(ei.definition.getType())); + } + } else if (ei.definition.getContentReference() != null) { + typeDefn = resolveNameReference(profile.getSnapshot(), ei.definition.getContentReference()); + } else if (ei.definition.getType().size() == 1 && ("Element".equals(ei.definition.getType().get(0).getWorkingCode()) || "BackboneElement".equals(ei.definition.getType().get(0).getWorkingCode()))) { + if (ei.definition.getType().get(0).hasProfile()) { + CanonicalType pu = ei.definition.getType().get(0).getProfile().get(0); + if (pu.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) + profiles.add(pu.getValue() + "#" + pu.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT)); + else + profiles.add(pu.getValue()); + } + } - private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException { - if (criteria.hasFixed()) { - List msgs = new ArrayList(); - checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); - return msgs.size() == 0; - } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { - throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + if (type != null) { + if (type.startsWith("@")) { + ei.definition = findElement(profile, type.substring(1)); + type = null; + } + } + NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); + if (debug) { + System.out.println(" " + localStack.getLiteralPath()); + } + String localStackLiterapPath = localStack.getLiteralPath(); + String eiPath = ei.getPath(); + assert (eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.getPath() + " - localStack.getLiteralPath: " + localStackLiterapPath; + boolean thisIsCodeableConcept = false; + String thisExtension = null; + boolean checkDisplay = true; + + checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.getElement(), localStack, true); + + ei.getElement().markValidation(profile, ei.definition); + boolean elementValidated = false; + if (type != null) { + if (isPrimitiveType(type)) { + checkPrimitive(hostContext, errors, ei.getPath(), type, ei.definition, ei.getElement(), profile, stack); } else { - throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + if (ei.definition.hasFixed()) { + checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getFixed(), profile.getUrl(), ei.definition.getSliceName(), null); + } + if (ei.definition.hasPattern()) { + checkFixedValue(errors, ei.getPath(), ei.getElement(), ei.definition.getPattern(), profile.getUrl(), ei.definition.getSliceName(), null, true); + } + } + if (type.equals("Identifier")) { + checkIdentifier(errors, ei.getPath(), ei.getElement(), ei.definition); + } else if (type.equals("Coding")) { + checkCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack); + } else if (type.equals("CodeableConcept")) { + checkDisplay = checkCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack); + thisIsCodeableConcept = true; + } else if (type.equals("Reference")) { + checkReference(hostContext, errors, ei.getPath(), ei.getElement(), profile, ei.definition, actualType, localStack); + // We only check extensions if we're not in a complex extension or if the element we're dealing with is not defined as part of that complex extension + } else if (type.equals("Extension")) { + Element eurl = ei.getElement().getNamedChild("url"); + if (rule(errors, IssueType.INVALID, ei.getPath(), eurl != null, I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) { + String url = eurl.primitiveValue(); + thisExtension = url; + if (rule(errors, IssueType.INVALID, ei.getPath(), !Utilities.noString(url), I18nConstants.EXTENSION_EXT_URL_NOTFOUND)) { + if (rule(errors, IssueType.INVALID, ei.getPath(), (extensionUrl != null) || Utilities.isAbsoluteUrl(url), I18nConstants.EXTENSION_EXT_URL_ABSOLUTE)) { + checkExtension(hostContext, errors, ei.getPath(), resource, element, ei.getElement(), ei.definition, profile, localStack, stack, extensionUrl); + } + } + } + } else if (type.equals("Resource")) { + validateContains(hostContext, errors, ei.getPath(), ei.definition, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if + elementValidated = true; + // (str.matches(".*([.,/])work\\1$")) + } else if (Utilities.isAbsoluteUrl(type)) { + StructureDefinition defn = context.fetchTypeDefinition(type); + if (defn != null && hasMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep())) { + List txtype = getMapping("http://hl7.org/fhir/terminology-pattern", defn, defn.getSnapshot().getElementFirstRep()); + if (txtype.contains("CodeableConcept")) { + checkTerminologyCodeableConcept(errors, ei.getPath(), ei.getElement(), profile, ei.definition, stack, defn); + thisIsCodeableConcept = true; + } else if (txtype.contains("Coding")) { + checkTerminologyCoding(errors, ei.getPath(), ei.getElement(), profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack, defn); + } + } + } + } else { + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), stack.getLiteralPath(), ei.definition != null, I18nConstants.VALIDATION_VAL_CONTENT_UNKNOWN, ei.getName())) + validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.getElement(), type, localStack, false, true, null); + } + StructureDefinition p = null; + String tail = null; + if (profiles.isEmpty()) { + if (type != null) { + p = getProfileForType(type, ei.definition.getType()); + + // If dealing with a primitive type, then we need to check the current child against + // the invariants (constraints) on the current element, because otherwise it only gets + // checked against the primary type's invariants: LLoyd + //if (p.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { + // checkInvariants(hostContext, errors, ei.path, profile, ei.definition, null, null, resource, ei.element); + //} + + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_NOTYPE, type); + } + } else if (profiles.size() == 1) { + String url = profiles.get(0); + if (url.contains("#")) { + tail = url.substring(url.indexOf("#") + 1); + url = url.substring(0, url.indexOf("#")); + } + p = this.context.fetchResource(StructureDefinition.class, url); + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, profiles.get(0)); + } else { + elementValidated = true; + HashMap> goodProfiles = new HashMap>(); + HashMap> badProfiles = new HashMap>(); + for (String typeProfile : profiles) { + String url = typeProfile; + tail = null; + if (url.contains("#")) { + tail = url.substring(url.indexOf("#") + 1); + url = url.substring(0, url.indexOf("#")); + } + p = this.context.fetchResource(StructureDefinition.class, typeProfile); + if (rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), p != null, I18nConstants.VALIDATION_VAL_UNKNOWN_PROFILE, typeProfile)) { + List profileErrors = new ArrayList(); + validateElement(hostContext, profileErrors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + if (hasErrors(profileErrors)) + badProfiles.put(typeProfile, profileErrors); + else + goodProfiles.put(typeProfile, profileErrors); + } + } + if (goodProfiles.size() == 1) { + errors.addAll(goodProfiles.values().iterator().next()); + } else if (goodProfiles.size() == 0) { + rule(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOMATCH, StringUtils.join("; ", profiles)); + for (String m : badProfiles.keySet()) { + p = this.context.fetchResource(StructureDefinition.class, m); + for (ValidationMessage message : badProfiles.get(m)) { + message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); + errors.add(message); + } + } + } else { + warning(errors, IssueType.STRUCTURE, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MULTIPLEMATCHES, StringUtils.join("; ", goodProfiles.keySet())); + for (String m : goodProfiles.keySet()) { + p = this.context.fetchResource(StructureDefinition.class, m); + for (ValidationMessage message : goodProfiles.get(m)) { + message.setMessage(message.getMessage() + " (validating against " + p.getUrl() + (p.hasVersion() ? "|" + p.getVersion() : "") + " [" + p.getName() + "])"); + errors.add(message); + } + } + } + } + if (p != null) { + trackUsage(p, hostContext, element); + + if (!elementValidated) { + if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + else + validateElement(hostContext, errors, p, getElementByTail(p, tail), profile, ei.definition, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + } + 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.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension); + } + } + } + } + + private void trackUsage(StructureDefinition profile, ValidatorHostContext hostContext, Element element) { + if (tracker != null) { + tracker.recordProfileUsage(profile, hostContext.getAppContext(), element); + } + } + + private boolean hasMapping(String url, StructureDefinition defn, ElementDefinition elem) { + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + return true; + } + } + + } + return false; + } + + private List getMapping(String url, StructureDefinition defn, ElementDefinition elem) { + List res = new ArrayList<>(); + String id = null; + for (StructureDefinitionMappingComponent m : defn.getMapping()) { + if (url.equals(m.getUri())) { + id = m.getIdentity(); + break; + } + } + if (id != null) { + for (ElementDefinitionMappingComponent m : elem.getMapping()) { + if (id.equals(m.getIdentity())) { + res.add(m.getMap()); + } + } + } + return res; + } + + public void checkMustSupport(StructureDefinition profile, ElementInfo ei) { + String usesMustSupport = profile.getUserString("usesMustSupport"); + if (usesMustSupport == null) { + usesMustSupport = "N"; + for (ElementDefinition pe : profile.getSnapshot().getElement()) { + if (pe.getMustSupport()) { + usesMustSupport = "Y"; + break; + } + } + profile.setUserData("usesMustSupport", usesMustSupport); + } + if (usesMustSupport.equals("Y")) { + String elementSupported = ei.getElement().getUserString("elementSupported"); + if (elementSupported == null || ei.definition.getMustSupport()) + if (ei.definition.getMustSupport()) { + ei.getElement().setUserData("elementSupported", "Y"); } } + } - private boolean yearIsValid(String v) { - if (v == null) { - return false; + public void checkCardinalities(List errors, StructureDefinition profile, Element element, NodeStack stack, + List childDefinitions, List children, List problematicPaths) throws DefinitionException { + // 3. report any definitions that have a cardinality problem + for (ElementDefinition ed : childDefinitions) { + if (ed.getRepresentation().isEmpty()) { // ignore xml attributes + int count = 0; + List slices = null; + if (ed.hasSlicing()) + slices = ProfileUtilities.getSliceList(profile, ed); + for (ElementInfo ei : children) + if (ei.definition == ed) + count++; + else if (slices != null) { + for (ElementDefinition sed : slices) { + if (ei.definition == sed) { + count++; + break; + } + } + } + String location = "Profile " + profile.getUrl() + ", Element '" + stack.getLiteralPath() + "." + tail(ed.getPath()) + (ed.hasSliceName() ? "[" + ed.getSliceName() + (ed.hasLabel() ? " (" + ed.getLabel() + ")" : "") + "]" : "") + "'"; + if (ed.getMin() > 0) { + if (problematicPaths.contains(ed.getPath())) + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, location, Integer.toString(ed.getMin())); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, location, Integer.toString(ed.getMin()), Integer.toString(count)); } + if (ed.hasMax() && !ed.getMax().equals("*")) { + if (problematicPaths.contains(ed.getPath())) + hint(errors, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMAX, location, ed.getMax()); + else + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), count <= Integer.parseInt(ed.getMax()), I18nConstants.VALIDATION_VAL_PROFILE_MAXIMUM, location, ed.getMax(), Integer.toString(count)); + } + } + } + } + + public List assignChildren(ValidatorHostContext hostContext, List errors, StructureDefinition profile, Element resource, + NodeStack stack, List childDefinitions, List children) throws DefinitionException { + // 2. assign children to a definition + // for each definition, for each child, check whether it belongs in the slice + ElementDefinition slicer = null; + boolean unsupportedSlicing = false; + List problematicPaths = new ArrayList(); + String slicingPath = null; + int sliceOffset = 0; + for (int i = 0; i < childDefinitions.size(); i++) { + ElementDefinition ed = childDefinitions.get(i); + boolean childUnsupportedSlicing = false; + boolean process = true; + if (ed.hasSlicing() && !ed.getSlicing().getOrdered()) + slicingPath = ed.getPath(); + else if (slicingPath != null && ed.getPath().equals(slicingPath)) + ; // nothing + else if (slicingPath != null && !ed.getPath().startsWith(slicingPath)) + slicingPath = null; + // where are we with slicing + if (ed.hasSlicing()) { + if (slicer != null && slicer.getPath().equals(ed.getPath())) { + String errorContext = "profile " + profile.getUrl(); + if (!resource.getChildValue("id").isEmpty()) + errorContext += "; instance " + resource.getChildValue("id"); + throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); + } + slicer = ed; + process = false; + sliceOffset = i; + } else if (slicer != null && !slicer.getPath().equals(ed.getPath())) + slicer = null; + + for (ElementInfo ei : children) { + if (ei.sliceInfo == null) { + ei.sliceInfo = new ArrayList<>(); + } + unsupportedSlicing = matchSlice(hostContext, errors, ei.sliceInfo, profile, stack, slicer, unsupportedSlicing, problematicPaths, sliceOffset, i, ed, childUnsupportedSlicing, ei); + } + } + int last = -1; + int lastSlice = -1; + for (ElementInfo ei : children) { + String sliceInfo = ""; + if (slicer != null) + sliceInfo = " (slice: " + slicer.getPath() + ")"; + if (!unsupportedSlicing) + if (ei.additionalSlice && ei.definition != null) { + if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) || + ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) { + slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), + "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + } + } else { + // Don't raise this if we're in an abstract profile, like Resource + if (!profile.getAbstract()) + rule(errors, IssueType.NOTSUPPORTED, ei.line(), ei.col(), ei.getPath(), (ei.definition != null), I18nConstants.VALIDATION_VAL_PROFILE_NOTALLOWED, profile.getUrl()); + } + // TODO: Should get the order of elements correct when parsing elements that are XML attributes vs. elements + boolean isXmlAttr = false; + if (ei.definition != null) { + for (Enumeration r : ei.definition.getRepresentation()) { + if (r.getValue() == PropertyRepresentation.XMLATTR) { + isXmlAttr = true; + break; + } + } + } + + if (!ToolingExtensions.readBoolExtension(profile, "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-no-order")) { + boolean ok = (ei.definition == null) || (ei.index >= last) || isXmlAttr; + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), ok, I18nConstants.VALIDATION_VAL_PROFILE_OUTOFORDER, profile.getUrl(), ei.getName()); + } + if (ei.slice != null && ei.index == last && ei.slice.getSlicing().getOrdered()) + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), (ei.definition == null) || (ei.sliceindex >= lastSlice) || isXmlAttr, I18nConstants.VALIDATION_VAL_PROFILE_SLICEORDER, profile.getUrl(), ei.getName()); + if (ei.definition == null || !isXmlAttr) + last = ei.index; + if (ei.slice != null) + lastSlice = ei.sliceindex; + else + lastSlice = -1; + } + return problematicPaths; + } + + public List listChildren(Element element, NodeStack stack) { + // 1. List the children, and remember their exact path (convenience) + List children = new ArrayList(); + ChildIterator iter = new ChildIterator(this, stack.getLiteralPath(), element); + while (iter.next()) + children.add(new ElementInfo(iter.name(), iter.element(), iter.path(), iter.count())); + return children; + } + + public void checkInvariants(ValidatorHostContext hostContext, List errors, StructureDefinition profile, ElementDefinition definition, Element resource, Element element, NodeStack stack, boolean onlyNonInherited) throws FHIRException { + checkInvariants(hostContext, errors, stack.getLiteralPath(), profile, definition, null, null, resource, element, onlyNonInherited); + } + + public boolean matchSlice(ValidatorHostContext hostContext, List errors, List sliceInfo, StructureDefinition profile, NodeStack stack, + ElementDefinition slicer, boolean unsupportedSlicing, List problematicPaths, int sliceOffset, int i, ElementDefinition ed, + boolean childUnsupportedSlicing, ElementInfo ei) { + boolean match = false; + if (slicer == null || slicer == ed) { + match = nameMatches(ei.getName(), tail(ed.getPath())); + } else { + if (nameMatches(ei.getName(), tail(ed.getPath()))) try { - int i = Integer.parseInt(v.substring(0, Math.min(4, v.length()))); - return i >= 1800 && i <= thisYear() + 80; - } catch (NumberFormatException e) { - return false; + match = sliceMatches(hostContext, ei.getElement(), ei.getPath(), slicer, ed, profile, errors, sliceInfo, stack); + if (match) { + ei.slice = slicer; + + // Since a defined slice was found, this is not an additional (undefined) slice. + ei.additionalSlice = false; + } else if (ei.slice == null) { + // if the specified slice is undefined, keep track of the fact this is an additional (undefined) slice, but only if a slice wasn't found previously + ei.additionalSlice = true; + } + } catch (FHIRException e) { + rule(errors, IssueType.PROCESSING, ei.line(), ei.col(), ei.getPath(), false, e.getMessage()); + unsupportedSlicing = true; + childUnsupportedSlicing = true; } } + if (match) { + boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", ""))); + if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, I18nConstants.VALIDATION_VAL_PROFILE_MATCHMULTIPLE, profile.getUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) { + ei.definition = ed; + if (ei.slice == null) { + ei.index = i; + } else { + ei.index = sliceOffset; + ei.sliceindex = i - (sliceOffset + 1); + } + } + } else if (childUnsupportedSlicing) { + problematicPaths.add(ed.getPath()); + } + return unsupportedSlicing; + } - private int thisYear() { - return Calendar.getInstance().get(Calendar.YEAR); + private ElementDefinition getElementByTail(StructureDefinition p, String tail) throws DefinitionException { + if (tail == null) + return p.getSnapshot().getElement().get(0); + for (ElementDefinition t : p.getSnapshot().getElement()) { + if (tail.equals(t.getId())) + return t; + } + throw new DefinitionException("Unable to find element with id '" + tail + "'"); + } + + private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { + if (isBundleEntry(ei.getPath())) { + Element req = ep.getNamedChild("request"); + Element resp = ep.getNamedChild("response"); + Element fullUrl = ep.getNamedChild("fullUrl"); + Element method = null; + Element url = null; + if (req != null) { + method = req.getNamedChild("method"); + url = req.getNamedChild("url"); + } + if (resp != null) { + return IdStatus.OPTIONAL; + } + if (method == null) { + if (fullUrl == null) + return IdStatus.REQUIRED; + else if (fullUrl.primitiveValue().startsWith("urn:uuid:") || fullUrl.primitiveValue().startsWith("urn:oid:")) + return IdStatus.OPTIONAL; + else + return IdStatus.REQUIRED; + } else { + String s = method.primitiveValue(); + if (s.equals("PUT")) { + if (url == null) + return IdStatus.REQUIRED; + else + return IdStatus.OPTIONAL; // or maybe prohibited? not clear + } else if (s.equals("POST")) + return IdStatus.OPTIONAL; // this should be prohibited, but see task 9102 + else // actually, we should never get to here; a bundle entry with method get/delete should not have a resource + return IdStatus.OPTIONAL; + } + } else if (isParametersEntry(ei.getPath()) || isBundleOutcome(ei.getPath())) + return IdStatus.OPTIONAL; + else + return IdStatus.REQUIRED; + } + + private void checkInvariants(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, ElementDefinition ed, String typename, String typeProfile, Element resource, Element element, boolean onlyNonInherited) throws FHIRException, FHIRException { + if (noInvariantChecks) + return; + + for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { + if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || profile.getUrl().equals(inv.getSource()))) { + @SuppressWarnings("unchecked") + Set invList = executionId.equals(element.getUserString(EXECUTION_ID)) ? (Set) element.getUserData(EXECUTED_CONSTRAINT_LIST) : null; + if (invList == null) { + invList = new HashSet<>(); + element.setUserData(EXECUTED_CONSTRAINT_LIST, invList); + element.setUserData(EXECUTION_ID, executionId); + } + if (!invList.contains(inv.getKey())) { + invList.add(inv.getKey()); + checkInvariant(hostContext, errors, path, profile, resource, element, inv); + } else { + //System.out.println("Skip "+inv.getKey()+" on "+path); + } + } + } + } + + public void checkInvariant(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { + ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); + if (n == null) { + long t = System.nanoTime(); + try { + n = fpe.parse(fixExpr(inv.getExpression())); + } catch (FHIRLexerException e) { + throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + } + fpeTime = fpeTime + (System.nanoTime() - t); + inv.setUserData("validator.expression.cache", n); } - public class NodeStack { - private ElementDefinition definition; - private Element element; - private ElementDefinition extension; - private String literalPath; // xpath format - private List logicalPaths; // dotted format, various entry points - private NodeStack parent; - private ElementDefinition type; - private String workingLang; + String msg; + boolean ok; + try { + long t = System.nanoTime(); + ok = fpe.evaluateToBoolean(hostContext, resource, hostContext.getRootResource(), element, n); + fpeTime = fpeTime + (System.nanoTime() - t); + msg = fpe.forLog(); + } catch (Exception ex) { + ok = false; + msg = ex.getMessage(); + } + if (!ok) { + if (!Utilities.noString(msg)) + msg = " (" + msg + ")"; + 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() + "]"); + else if (bpWarnings == BestPracticeWarningLevel.Warning) + warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + else if (bpWarnings == BestPracticeWarningLevel.Error) + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } else if (inv.getSeverity() == ConstraintSeverity.ERROR) { + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } else if (inv.getSeverity() == ConstraintSeverity.WARNING) { + warning(errors, IssueType.INVARIANT, element.line(), element.line(), path, ok, inv.getKey() + ": " + inv.getHuman() + msg + " [" + n.toString() + "]"); + } + } + } - public NodeStack() { + private void validateMessage(List errors, List entries, Element messageHeader, NodeStack stack, String fullUrl, String id) { + // first entry must be a messageheader + if (rule(errors, IssueType.INVALID, messageHeader.line(), messageHeader.col(), stack.getLiteralPath(), messageHeader.getType().equals("MessageHeader"), I18nConstants.VALIDATION_BUNDLE_MESSAGE)) { + List elements = messageHeader.getChildren("focus"); + for (Element elem : elements) + validateBundleReference(errors, entries, elem, "MessageHeader Data", stack.push(elem, -1, null, null), fullUrl, "MessageHeader", id); + } + } + + private void validateObservation(List errors, Element element, NodeStack stack) { + // all observations should have a subject, a performer, and a time + + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); + List performers = new ArrayList<>(); + element.getNamedChildren("performer", performers); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, + "All observations should have an effectiveDateTime or an effectivePeriod"); + } + + /* + * The actual base entry point for internal use (re-entrant) + */ + private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { + assert stack != null; + assert resource != null; + boolean ok = true; + String resourceName = element.getType(); // todo: consider namespace...? + if (defn == null) { + long t = System.nanoTime(); + defn = element.getProperty().getStructure(); + if (defn == null) + defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); + sdTime = sdTime + (System.nanoTime() - t); + ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, I18nConstants.VALIDATION_VAL_PROFILE_NODEFINITION, resourceName); + } + + String type = defn.getKind() == StructureDefinitionKind.LOGICAL ? defn.getId() : defn.getType(); + // special case: we have a bundle, and the profile is not for a bundle. We'll try the first entry instead + if (!type.equals(resourceName) && resourceName.equals("Bundle")) { + NodeStack first = getFirstEntry(stack); + if (first != null && first.getElement().getType().equals(type)) { + element = first.element; + stack = first; + resourceName = element.getType(); + idstatus = IdStatus.OPTIONAL; // why? + } + // todo: validate everything in this bundle. + } + ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, type, resourceName); + + if (ok) { + if (idstatus == IdStatus.REQUIRED && (element.getNamedChild("id") == null)) + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MISSING); + else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild("id") != null)) + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_PROHIBITED); + start(hostContext, errors, element, element, defn, stack); // root is both definition and type + } + } + + private NodeStack getFirstEntry(NodeStack bundle) { + List list = new ArrayList(); + bundle.getElement().getNamedChildren("entry", list); + if (list.isEmpty()) + return null; + Element resource = list.get(0).getNamedChild("resource"); + if (resource == null) + return null; + else { + NodeStack entry = bundle.push(list.get(0), 0, list.get(0).getProperty().getDefinition(), list.get(0).getProperty().getDefinition()); + return entry.push(resource, -1, resource.getProperty().getDefinition(), context.fetchTypeDefinition(resource.fhirType()).getSnapshot().getElementFirstRep()); + } + } + + private void validateSections(List errors, List entries, Element focus, NodeStack stack, String fullUrl, String id) { + List sections = new ArrayList(); + focus.getNamedChildren("section", sections); + int i = 1; + for (Element section : sections) { + NodeStack localStack = stack.push(section, i, null, null); + + // technically R4+, but there won't be matches from before that + validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "author", "Section"); + validateDocumentReference(errors, entries, section, stack, fullUrl, id, false, "focus", "Section"); + + List sectionEntries = new ArrayList(); + section.getNamedChildren("entry", sectionEntries); + int j = 1; + for (Element sectionEntry : sectionEntries) { + NodeStack localStack2 = localStack.push(sectionEntry, j, null, null); + validateBundleReference(errors, entries, sectionEntry, "Section Entry", localStack2, fullUrl, "Composition", id); + j++; + } + validateSections(errors, entries, section, localStack, fullUrl, id); + i++; + } + } + + private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException { + if (criteria.hasFixed()) { + List msgs = new ArrayList(); + checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); + return msgs.size() == 0; + } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { + throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + } else { + throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + } + } + + private boolean yearIsValid(String v) { + if (v == null) { + return false; + } + try { + int i = Integer.parseInt(v.substring(0, Math.min(4, v.length()))); + return i >= 1800 && i <= thisYear() + 80; + } catch (NumberFormatException e) { + return false; + } + } + + private int thisYear() { + return Calendar.getInstance().get(Calendar.YEAR); + } + + public class NodeStack { + private ElementDefinition definition; + private Element element; + private ElementDefinition extension; + private String literalPath; // xpath format + private List logicalPaths; // dotted format, various entry points + private NodeStack parent; + private ElementDefinition type; + private String workingLang; + + public NodeStack() { + } + + public NodeStack(Element element) { + this.element = element; + literalPath = element.getName(); + workingLang = validationLanguage; + if (!element.getName().equals(element.fhirType())) { + logicalPaths = new ArrayList<>(); + logicalPaths.add(element.fhirType()); + } + } + + public NodeStack(Element element, String refPath) { + this.element = element; + literalPath = refPath + "->" + element.getName(); + workingLang = validationLanguage; + } + + public String addToLiteralPath(String... path) { + StringBuilder b = new StringBuilder(); + b.append(getLiteralPath()); + for (String p : path) { + if (p.startsWith(":")) { + b.append("["); + b.append(p.substring(1)); + b.append("]"); + } else { + b.append("."); + b.append(p); } + } + return b.toString(); + } - public NodeStack(Element element) { - this.element = element; - literalPath = element.getName(); - workingLang = validationLanguage; - if (!element.getName().equals(element.fhirType())) { - logicalPaths = new ArrayList<>(); - logicalPaths.add(element.fhirType()); + private ElementDefinition getDefinition() { + return definition; + } + + private Element getElement() { + return element; + } + + protected String getLiteralPath() { + return literalPath == null ? "" : literalPath; + } + + private List getLogicalPaths() { + return logicalPaths == null ? new ArrayList() : logicalPaths; + } + + private ElementDefinition getType() { + return type; + } + + private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) { + return pushInternal(element, count, definition, type, "->"); + } + + private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) { + return pushInternal(element, count, definition, type, "."); + } + + private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) { + NodeStack res = new NodeStack(); + res.parent = this; + res.workingLang = this.workingLang; + res.element = element; + res.definition = definition; + res.literalPath = getLiteralPath() + sep + element.getName(); + if (count > -1) + res.literalPath = res.literalPath + "[" + Integer.toString(count) + "]"; + else if (element.getSpecial() == null && element.getProperty().isList()) + res.literalPath = res.literalPath + "[0]"; + else if (element.getProperty().isChoice()) { + String n = res.literalPath.substring(res.literalPath.lastIndexOf(".") + 1); + String en = element.getProperty().getName(); + en = en.substring(0, en.length() - 3); + String t = n.substring(en.length()); + if (isPrimitiveType(Utilities.uncapitalize(t))) + t = Utilities.uncapitalize(t); + res.literalPath = res.literalPath.substring(0, res.literalPath.lastIndexOf(".")) + "." + en + ".ofType(" + t + ")"; + } + res.logicalPaths = new ArrayList(); + if (type != null) { + // type will be bull if we on a stitching point of a contained resource, or if.... + res.type = type; + String tn = res.type.getPath(); + String t = tail(definition.getPath()); + if ("Resource".equals(tn)) { + tn = element.fhirType(); + } + for (String lp : getLogicalPaths()) { + res.logicalPaths.add(lp + "." + t); + if (t.endsWith("[x]")) + res.logicalPaths.add(lp + "." + t.substring(0, t.length() - 3) + type.getPath()); + } + res.logicalPaths.add(tn); + } else if (definition != null) { + for (String lp : getLogicalPaths()) { + res.logicalPaths.add(lp + "." + element.getName()); + } + res.logicalPaths.add(definition.typeSummary()); + } else + res.logicalPaths.addAll(getLogicalPaths()); + return res; + } + + private void setType(ElementDefinition type) { + this.type = type; + } + } + + public String reportTimes() { + String s = String.format("Times (ms): overall = %d, tx = %d, sd = %d, load = %d, fpe = %d", overall / 1000000, txTime / 1000000, sdTime / 1000000, loadTime / 1000000, fpeTime / 1000000); + overall = 0; + txTime = 0; + sdTime = 0; + loadTime = 0; + fpeTime = 0; + return s; + } + + public boolean isNoBindingMsgSuppressed() { + return noBindingMsgSuppressed; + } + + public IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) { + this.noBindingMsgSuppressed = noBindingMsgSuppressed; + return this; + } + + + public boolean isNoTerminologyChecks() { + return noTerminologyChecks; + } + + public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks) { + this.noTerminologyChecks = noTerminologyChecks; + return this; + } + + public void checkAllInvariants() { + for (StructureDefinition sd : context.allStructures()) { + if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { + if (inv.hasExpression()) { + try { + ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); + if (n == null) { + n = fpe.parse(fixExpr(inv.getExpression())); + inv.setUserData("validator.expression.cache", n); + } + fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); + } catch (Exception e) { + System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage()); + } } + } } + } + } + } - public NodeStack(Element element, String refPath) { - this.element = element; - literalPath = refPath + "->" + element.getName(); - workingLang = validationLanguage; - } - - public String addToLiteralPath(String... path) { - StringBuilder b = new StringBuilder(); - b.append(getLiteralPath()); - for (String p : path) { - if (p.startsWith(":")) { - b.append("["); - b.append(p.substring(1)); - b.append("]"); - } else { - b.append("."); - b.append(p); - } - } - return b.toString(); - } - - private ElementDefinition getDefinition() { - return definition; - } - - private Element getElement() { - return element; - } - - protected String getLiteralPath() { - return literalPath == null ? "" : literalPath; - } - - private List getLogicalPaths() { - return logicalPaths == null ? new ArrayList() : logicalPaths; - } - - private ElementDefinition getType() { - return type; - } - - private NodeStack pushTarget(Element element, int count, ElementDefinition definition, ElementDefinition type) { - return pushInternal(element, count, definition, type, "->"); - } - - private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) { - return pushInternal(element, count, definition, type, "."); - } - - private NodeStack pushInternal(Element element, int count, ElementDefinition definition, ElementDefinition type, String sep) { - NodeStack res = new NodeStack(); - res.parent = this; - res.workingLang = this.workingLang; - res.element = element; - res.definition = definition; - res.literalPath = getLiteralPath() + sep + element.getName(); - if (count > -1) - res.literalPath = res.literalPath + "[" + Integer.toString(count) + "]"; - else if (element.getSpecial() == null && element.getProperty().isList()) - res.literalPath = res.literalPath + "[0]"; - else if (element.getProperty().isChoice()) { - String n = res.literalPath.substring(res.literalPath.lastIndexOf(".") + 1); - String en = element.getProperty().getName(); - en = en.substring(0, en.length() - 3); - String t = n.substring(en.length()); - if (isPrimitiveType(Utilities.uncapitalize(t))) - t = Utilities.uncapitalize(t); - res.literalPath = res.literalPath.substring(0, res.literalPath.lastIndexOf(".")) + "." + en + ".ofType(" + t + ")"; - } - res.logicalPaths = new ArrayList(); - if (type != null) { - // type will be bull if we on a stitching point of a contained resource, or if.... - res.type = type; - String tn = res.type.getPath(); - String t = tail(definition.getPath()); - if ("Resource".equals(tn)) { - tn = element.fhirType(); - } - for (String lp : getLogicalPaths()) { - res.logicalPaths.add(lp + "." + t); - if (t.endsWith("[x]")) - res.logicalPaths.add(lp + "." + t.substring(0, t.length() - 3) + type.getPath()); - } - res.logicalPaths.add(tn); - } else if (definition != null) { - for (String lp : getLogicalPaths()) { - res.logicalPaths.add(lp + "." + element.getName()); - } - res.logicalPaths.add(definition.typeSummary()); - } else - res.logicalPaths.addAll(getLogicalPaths()); - return res; - } - - private void setType(ElementDefinition type) { - this.type = type; - } + private String fixExpr(String expr) { + // this is a hack work around for past publication of wrong FHIRPath expressions + // R4 + // waiting for 4.0.2 + if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) { + return "probablility.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))"; } - public String reportTimes() { - String s = String.format("Times (ms): overall = %d, tx = %d, sd = %d, load = %d, fpe = %d", overall / 1000000, txTime / 1000000, sdTime / 1000000, loadTime / 1000000, fpeTime / 1000000); - overall = 0; - txTime = 0; - sdTime = 0; - loadTime = 0; - fpeTime = 0; - return s; - } + // handled in 4.0.1 + if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) + return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())"; + if ("isModifier implies isModifierReason.exists()".equals(expr)) + return "(isModifier.exists() and isModifier) implies isModifierReason.exists()"; + if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) + return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))"; + if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) + return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()"; + if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) + return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()"; - public boolean isNoBindingMsgSuppressed() { - return noBindingMsgSuppressed; - } + // R3 + if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')"; + if ("value.empty() or code!=component.code".equals(expr)) + return "value.empty() or (code in component.code).not()"; + if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; + if ("element.all(definition and min and max)".equals(expr)) + return "element.all(definition.exists() and min.exists() and max.exists())"; + if ("telecom or endpoint".equals(expr)) + return "telecom.exists() or endpoint.exists()"; + if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) + return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; + if ("searchType implies type = 'string'".equals(expr)) + return "searchType.exists() implies type = 'string'"; + if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) + return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')"; + if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) + return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; - public IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) { - this.noBindingMsgSuppressed = noBindingMsgSuppressed; - return this; - } + if ("".equals(expr)) + return ""; + return expr; + } + public IEvaluationContext getExternalHostServices() { + return externalHostServices; + } - public boolean isNoTerminologyChecks() { - return noTerminologyChecks; - } + public String getValidationLanguage() { + return validationLanguage; + } - public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks) { - this.noTerminologyChecks = noTerminologyChecks; - return this; - } + public void setValidationLanguage(String validationLanguage) { + this.validationLanguage = validationLanguage; + } - public void checkAllInvariants() { - for (StructureDefinition sd : context.allStructures()) { - if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { - for (ElementDefinition ed : sd.getSnapshot().getElement()) { - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasExpression()) { - try { - ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); - if (n == null) { - n = fpe.parse(fixExpr(inv.getExpression())); - inv.setUserData("validator.expression.cache", n); - } - fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n); - } catch (Exception e) { - System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " (\"" + inv.getExpression() + "\"): " + e.getMessage()); - } - } - } - } - } - } - } + public boolean isDebug() { + return debug; + } - private String fixExpr(String expr) { - // this is a hack work around for past publication of wrong FHIRPath expressions - // R4 - // waiting for 4.0.2 - if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) { - return "probablility.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))"; - } - - // handled in 4.0.1 - if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) - return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())"; - if ("isModifier implies isModifierReason.exists()".equals(expr)) - return "(isModifier.exists() and isModifier) implies isModifierReason.exists()"; - if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) - return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))"; - if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) - return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()"; - if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) - return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()"; - - // R3 - if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')"; - if ("value.empty() or code!=component.code".equals(expr)) - return "value.empty() or (code in component.code).not()"; - if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; - if ("element.all(definition and min and max)".equals(expr)) - return "element.all(definition.exists() and min.exists() and max.exists())"; - if ("telecom or endpoint".equals(expr)) - return "telecom.exists() or endpoint.exists()"; - if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) - return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; - if ("searchType implies type = 'string'".equals(expr)) - return "searchType.exists() implies type = 'string'"; - if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) - return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')"; - if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) - return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; - - if ("".equals(expr)) - return ""; - return expr; - } - - public IEvaluationContext getExternalHostServices() { - return externalHostServices; - } - - public String getValidationLanguage() { - return validationLanguage; - } - - public void setValidationLanguage(String validationLanguage) { - this.validationLanguage = validationLanguage; - } - - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } + public void setDebug(boolean debug) { + this.debug = debug; + } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java new file mode 100644 index 000000000..0cbc9ded6 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -0,0 +1,229 @@ +package org.hl7.fhir.validation.utils; + +public class I18nConstants { + + public final static String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited"; + public final static String RESOURCE_RES_ID_MISSING = "Resource_RES_ID_Missing"; + public final static String VALIDATION_VAL_PROFILE_WRONGTYPE = "Validation_VAL_Profile_WrongType"; + public final static String VALIDATION_VAL_PROFILE_NODEFINITION = "Validation_VAL_Profile_NoDefinition"; + public final static String VALIDATION_BUNDLE_MESSAGE = "Validation_BUNDLE_Message"; + public final static String VALIDATION_VAL_PROFILE_MATCHMULTIPLE = "Validation_VAL_Profile_MatchMultiple"; + public final static String VALIDATION_VAL_PROFILE_SLICEORDER = "Validation_VAL_Profile_SliceOrder"; + public final static String VALIDATION_VAL_PROFILE_OUTOFORDER = "Validation_VAL_Profile_OutOfOrder"; + public final static String VALIDATION_VAL_PROFILE_NOTALLOWED = "Validation_VAL_Profile_NotAllowed"; + public final static String VALIDATION_VAL_PROFILE_NOTSLICE = "Validation_VAL_Profile_NotSlice"; + public final static String VALIDATION_VAL_PROFILE_MAXIMUM = "Validation_VAL_Profile_Maximum"; + public final static String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax"; + public final static String VALIDATION_VAL_PROFILE_MINIMUM = "Validation_VAL_Profile_Minimum"; + public final static String VALIDATION_VAL_PROFILE_NOCHECKMIN = "Validation_VAL_Profile_NoCheckMin"; + public final static String VALIDATION_VAL_PROFILE_MULTIPLEMATCHES = "Validation_VAL_Profile_MultipleMatches"; + public final static String VALIDATION_VAL_PROFILE_NOMATCH = "Validation_VAL_Profile_NoMatch"; + public final static String VALIDATION_VAL_UNKNOWN_PROFILE = "Validation_VAL_Unknown_Profile"; + public final static String VALIDATION_VAL_NOTYPE = "Validation_VAL_NoType"; + public final static String VALIDATION_VAL_CONTENT_UNKNOWN = "Validation_VAL_Content_Unknown"; + public final static String EXTENSION_EXT_URL_ABSOLUTE = "Extension_EXT_URL_Absolute"; + public final static String EXTENSION_EXT_URL_NOTFOUND = "Extension_EXT_Url_NotFound"; + public final static String VALIDATION_VAL_PROFILE_NOTYPE = "Validation_VAL_Profile_NoType"; + public final static String BUNDLE_BUNDLE_ENTRY_DOCUMENT = "Bundle_BUNDLE_Entry_Document"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE3 = "Bundle_BUNDLE_Entry_Type3"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE2 = "Bundle_BUNDLE_Entry_Type2"; + public final static String BUNDLE_BUNDLE_ENTRY_NOPROFILE = "Bundle_BUNDLE_Entry_NoProfile"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type"; + public final static String BUNDLE_BUNDLE_ENTRY_NOTFOUND = "Bundle_BUNDLE_Entry_NotFound"; + public final static String BUNDLE_BUNDLE_ENTRY_ORPHAN = "Bundle_BUNDLE_Entry_Orphan"; + public final static String BUNDLE_BUNDLE_ENTRY_IDURLMISMATCH = "Bundle_BUNDLE_Entry_IdUrlMismatch"; + public final static String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical"; + public final static String BUNDLE_BUNDLE_ENTRY_MISMATCHIDURL = "Bundle_BUNDLE_Entry_MismatchIdUrl"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE = "Bundle_BUNDLE_Entry_NoFirstResource"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFIRST = "Bundle_BUNDLE_Entry_NoFirst"; + public final static String QUESTIONNAIRE_QR_ITEM_CODINGNOOPTIONS = "Questionnaire_QR_Item_CodingNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NOCODING = "Questionnaire_QR_Item_NoCoding"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSCODING = "Questionnaire_QR_Item_NoOptionsCoding"; + public final static String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NOSTRING = "Questionnaire_QR_Item_NoString"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING = "Questionnaire_QR_Item_NoOptionsString"; + public final static String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTIME = "Questionnaire_QR_Item_NoTime"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSTIME = "Questionnaire_QR_Item_NoOptionsTime"; + public final static String QUESTIONNAIRE_QR_ITEM_DATENOOPTIONS = "Questionnaire_QR_Item_DateNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NODATE = "Questionnaire_QR_Item_NoDate"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSDATE = "Questionnaire_QR_Item_NoOptionsDate"; + public final static String QUESTIONNAIRE_QR_ITEM_INTNOOPTIONS = "Questionnaire_QR_Item_IntNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NOINTEGER = "Questionnaire_QR_Item_NoInteger"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSINTEGER = "Questionnaire_QR_Item_NoOptionsInteger"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONS = "Questionnaire_QR_Item_NoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_CODING = "Questionnaire_QR_Item_Coding"; + public final static String QUESTIONNAIRE_QR_ITEM_BADOPTION = "Questionnaire_QR_Item_BadOption"; + public final static String TERMINOLOGY_TX_VALUESET_NOTFOUND = "Terminology_TX_ValueSet_NotFound"; + public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE2 = "Questionnaire_QR_Item_WrongType2"; + public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED2 = "Questionnaire_QR_Item_NotEnabled2"; + public final static String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTFOUND = "Questionnaire_QR_Item_NotFound"; + public final static String QUESTIONNAIRE_QR_ITEM_NOLINKID = "Questionnaire_QR_Item_NoLinkId"; + public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI"; + public final static String QUESTIONNAIRE_QR_ITEM_DISPLAY = "Questionnaire_QR_Item_Display"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTYPE = "Questionnaire_QR_Item_NoType"; + public final static String QUESTIONNAIRE_QR_ITEM_GROUP = "Questionnaire_QR_Item_Group"; + public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEA = "Questionnaire_QR_Item_OnlyOneA"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED = "Questionnaire_QR_Item_NotEnabled"; + public final static String QUESTIONNAIRE_QR_ITEM_MISSING = "Questionnaire_QR_Item_Missing"; + public final static String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text"; + public final static String QUESTIONNAIRE_QR_Q_NOTFOUND = "Questionnaire_QR_Q_NotFound"; + public final static String QUESTIONNAIRE_QR_Q_NONE = "Questionnaire_QR_Q_None"; + public final static String CODESYSTEM_CS_VS_INCLUDEDETAILS = "CodeSystem_CS_VS_IncludeDetails"; + public final static String CODESYSTEM_CS_VS_WRONGSYSTEM = "CodeSystem_CS_VS_WrongSystem"; + public final static String CODESYSTEM_CS_VS_INVALID = "CodeSystem_CS_VS_Invalid"; + public final static String CODESYSTEM_CS_VS_MISMATCH = "CodeSystem_CS_VS_MisMatch"; + public final static String CAPABALITYSTATEMENT_CS_SP_WRONGTYPE = "CapabalityStatement_CS_SP_WrongType"; + public final static String META_RES_SECURITY_DUPLICATE = "Meta_RES_Security_Duplicate"; + public final static String LANGUAGE_XHTML_LANG_DIFFERENT2 = "Language_XHTML_Lang_Different2"; + public final static String LANGUAGE_XHTML_LANG_MISSING3 = "Language_XHTML_Lang_Missing3"; + public final static String LANGUAGE_XHTML_LANG_DIFFERENT1 = "Language_XHTML_Lang_Different1"; + public final static String LANGUAGE_XHTML_LANG_MISSING2 = "Language_XHTML_Lang_Missing2"; + public final static String LANGUAGE_XHTML_LANG_MISSING1 = "Language_XHTML_Lang_Missing1"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_AFTER = "Questionnaire_Q_EnableWhen_After"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_SELF = "Questionnaire_Q_EnableWhen_Self"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOTARGET = "Questionnaire_Q_EnableWhen_NoTarget"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_ISINNER = "Questionnaire_Q_EnableWhen_IsInner"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOLINK = "Questionnaire_Q_EnableWhen_NoLink"; + public final static String VALIDATION_VAL_PROFILE_NOSNAPSHOT = "Validation_VAL_Profile_NoSnapshot"; + public final static String VALIDATION_VAL_PROFILE_UNKNOWN = "Validation_VAL_Profile_Unknown"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFULLURL = "Bundle_BUNDLE_Entry_NoFullUrl"; + public final static String BUNDLE_BUNDLE_NOT_LOCAL = "Bundle_BUNDLE_Not_Local"; + public final static String REFERENCE_REF_RESOURCETYPE = "Reference_REF_ResourceType"; + public final static String BUNDLE_BUNDLE_FULLURL_NEEDVERSION = "Bundle_BUNDLE_FullUrl_NeedVersion"; + public final static String BUNDLE_BUNDLE_MULTIPLEMATCHES = "Bundle_BUNDLE_MultipleMatches"; + public final static String REFERENCE_REF_FORMAT2 = "Reference_REF_Format2"; + public final static String REFERENCE_REF_FORMAT1 = "Reference_REF_Format1"; + public final static String BUNDLE_BUNDLE_FULLURL_MISSING = "Bundle_BUNDLE_FullUrl_Missing"; + public final static String BUNDLE_MSG_EVENT_COUNT = "Bundle_MSG_Event_Count"; + public final static String REFERENCE_REF_BADTARGETTYPE2 = "Reference_REF_BadTargetType2"; + public final static String REFERENCE_REF_NOTFOUND_BUNDLE = "Reference_REF_NotFound_Bundle"; + public final static String REFERENCE_REF_AGGREGATION = "Reference_REF_Aggregation"; + public final static String REFERENCE_REF_BADTARGETTYPE = "Reference_REF_BadTargetType"; + public final static String REFERENCE_REF_MULTIPLEMATCHES = "Reference_REF_MultipleMatches"; + public final static String REFERENCE_REF_CANTMATCHCHOICE = "Reference_REF_CantMatchChoice"; + public final static String REFERENCE_REF_CANTMATCHTYPE = "Reference_REF_CantMatchType"; + public final static String REFERENCE_REF_CANTRESOLVEPROFILE = "Reference_REF_CantResolveProfile"; + public final static String REFERENCE_REF_NOTYPE = "Reference_REF_NoType"; + public final static String REFERENCE_REF_WRONGTARGET = "Reference_REF_WrongTarget"; + public final static String REFERENCE_REF_CANTRESOLVE = "Reference_REF_CantResolve"; + public final static String REFERENCE_REF_NODISPLAY = "Reference_REF_NoDisplay"; + public final static String TERMINOLOGY_TX_BINDING_NOSOURCE2 = "Terminology_TX_Binding_NoSource2"; + public final static String TERMINOLOGY_TX_NOVALID_18 = "Terminology_TX_NoValid_18"; + public final static String TERMINOLOGY_TX_NOVALID_17 = "Terminology_TX_NoValid_17"; + public final static String TERMINOLOGY_TX_NOVALID_16 = "Terminology_TX_NoValid_16"; + public final static String TERMINOLOGY_TX_NOVALID_15 = "Terminology_TX_NoValid_15"; + public final static String TERMINOLOGY_TX_VALUESET_NOTFOUND2 = "Terminology_TX_ValueSet_NotFound2"; + public final static String XHTML_XHTML_NS_INVALID = "XHTML_XHTML_NS_InValid"; + public final static String XHTML_XHTML_ATTRIBUTE_ILLEGAL = "XHTML_XHTML_Attribute_Illegal"; + public final static String XHTML_XHTML_ELEMENT_ILLEGAL = "XHTML_XHTML_Element_Illegal"; + public final static String XHTML_XHTML_NAME_INVALID = "XHTML_XHTML_Name_Invalid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH = "Type_Specific_Checks_DT_Primitive_Length"; + public final static String TYPE_SPECIFIC_CHECKS_DT_CODE_WS = "Type_Specific_Checks_DT_Code_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID = "Type_Specific_Checks_DT_Instant_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE = "Type_Specific_Checks_DT_DateTime_Reasonable"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX = "Type_Specific_Checks_DT_DateTime_Regex"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE = "Type_Specific_Checks_DT_Decimal_Range"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID = "Type_Specific_Checks_DT_Decimal_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1 = "Type_Specific_Checks_DT_Integer_LT1"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0 = "Type_Specific_Checks_DT_Integer_LT0"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT = "Type_Specific_Checks_DT_Integer_LT"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_GT = "Type_Specific_Checks_DT_Integer_GT"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER64_VALID = "Type_Specific_Checks_DT_Integer64_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_VALID = "Type_Specific_Checks_DT_Integer_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_BASE64_VALID = "Type_Specific_Checks_DT_Base64_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATE_VALID = "Type_Specific_Checks_DT_Date_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_TIME_VALID = "Type_Specific_Checks_DT_Time_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID = "Type_Specific_Checks_DT_DateTime_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ = "Type_Specific_Checks_DT_DateTime_TZ"; + public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH = "Type_Specific_Checks_DT_String_Length"; + public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_WS = "Type_Specific_Checks_DT_String_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY = "Type_Specific_Checks_DT_Primitive_NotEmpty"; + public final static String TYPE_SPECIFIC_CHECKS_DT_ID_VALID = "Type_Specific_Checks_DT_ID_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE = "Type_Specific_Checks_DT_URL_Resolve"; + public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_VAID = "Type_Specific_Checks_DT_UUID_Vaid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT = "Type_Specific_Checks_DT_UUID_Strat"; + public final static String TYPE_SPECIFIC_CHECKS_DT_OID_VALID = "Type_Specific_Checks_DT_OID_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_OID_START = "Type_Specific_Checks_DT_OID_Start"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_WS = "Type_Specific_Checks_DT_URI_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_OID = "Type_Specific_Checks_DT_URI_OID"; + public final static String TYPE_SPECIFIC_CHECKS_DT_BOOLEAN_VALUE = "Type_Specific_Checks_DT_Boolean_Value"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX = "Type_Specific_Checks_DT_Primitive_Regex"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS = "Type_Specific_Checks_DT_Primitive_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT = "Type_Specific_Checks_DT_Primitive_ValueExt"; + public final static String TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM = "Type_Specific_Checks_DT_Identifier_System"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_SUFFIX = "Fixed_Type_Checks_DT_Name_Suffix"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_PREFIX = "Fixed_Type_Checks_DT_Name_Prefix"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_GIVEN = "Fixed_Type_Checks_DT_Name_Given"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_FAMILY = "Fixed_Type_Checks_DT_Name_Family"; + public final static String EXTENSION_EXT_COUNT_NOTFOUND = "Extension_EXT_Count_NotFound"; + public final static String EXTENSION_EXT_COUNT_MISMATCH = "Extension_EXT_Count_Mismatch"; + public final static String EXTENSION_EXT_FIXED_BANNED = "Extension_EXT_Fixed_Banned"; + public final static String INTERNAL_INT_BAD_TYPE = "Internal_INT_Bad_Type"; + public final static String _DT_FIXED_WRONG = "_DT_Fixed_Wrong"; + public final static String PROFILE_VAL_MISSINGELEMENT = "Profile_VAL_MissingElement"; + public final static String PROFILE_VAL_NOTALLOWED = "Profile_VAL_NotAllowed"; + public final static String PROFILE_EXT_NOT_HERE = "Profile_EXT_Not_Here"; + public final static String EXTENSION_EXT_CONTEXT_WRONG = "Extension_EXT_Context_Wrong"; + public final static String EXTENSION_EXT_TYPE = "Extension_EXT_Type"; + public final static String EXTENSION_EXT_SIMPLE = "Extension_EXT_Simple"; + public final static String EXTENSION_EXT_MODIFIER_N = "Extension_EXT_Modifier_N"; + public final static String EXTENSION_EXT_MODIFIER_Y = "Extension_EXT_Modifier_Y"; + public final static String EXTENSION_EXT_MODIFIER_MISMATCHN = "Extension_EXT_Modifier_MismatchN"; + public final static String EXTENSION_EXT_MODIFIER_MISMATCHY = "Extension_EXT_Modifier_MismatchY"; + public final static String EXTENSION_EXT_UNKNOWN = "Extension_EXT_Unknown"; + public final static String EXTENSION_EXT_UNKNOWN_NOTHERE = "Extension_EXT_Unknown_NotHere"; + public final static String EXTENSION_EXT_SUBEXTENSION_INVALID = "Extension_EXT_SubExtension_Invalid"; + public final static String EXTENSION_EXT_VERSION_INTERNAL = "Extension_EXT_Version_Internal"; + public final static String EXTENSION_EXT_VERSION_NOCHANGE = "Extension_EXT_Version_NoChange"; + public final static String EXTENSION_EXT_VERSION_INVALIDID = "Extension_EXT_Version_InvalidId"; + public final static String EXTENSION_EXT_VERSION_INVALID = "Extension_EXT_Version_Invalid"; + public final static String TERMINOLOGY_TX_ERROR_CODING2 = "Terminology_TX_Error_Coding2"; + public final static String TERMINOLOGY_TX_BINDING_NOSOURCE = "Terminology_TX_Binding_NoSource"; + public final static String TERMINOLOGY_TX_BINDING_CANTCHECK = "Terminology_TX_Binding_CantCheck"; + public final static String TERMINOLOGY_TX_ERROR_CODING1 = "Terminology_TX_Error_Coding1"; + public final static String TERMINOLOGY_TX_NOVALID_14 = "Terminology_TX_NoValid_14"; + public final static String TERMINOLOGY_TX_NOVALID_13 = "Terminology_TX_NoValid_13"; + public final static String TERMINOLOGY_TX_NOVALID_12 = "Terminology_TX_NoValid_12"; + public final static String TERMINOLOGY_TX_CONFIRM_6 = "Terminology_TX_Confirm_6"; + public final static String TERMINOLOGY_TX_CONFIRM_5 = "Terminology_TX_Confirm_5"; + public final static String TERMINOLOGY_TX_CONFIRM_4 = "Terminology_TX_Confirm_4"; + public final static String TERMINOLOGY_TX_BINDING_NOSERVER = "Terminology_TX_Binding_NoServer"; + public final static String TERMINOLOGY_TX_BINDING_MISSING2 = "Terminology_TX_Binding_Missing2"; + public final static String TERMINOLOGY_TX_SYSTEM_VALUESET2 = "Terminology_TX_System_ValueSet2"; + public final static String TERMINOLOGY_TX_SYSTEM_RELATIVE = "Terminology_TX_System_Relative"; + public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX = "Terminology_TX_Error_CodeableConcept_Max"; + public final static String TERMINOLOGY_TX_NOVALID_11 = "Terminology_TX_NoValid_11"; + public final static String TERMINOLOGY_TX_NOVALID_9 = "Terminology_TX_NoValid_9"; + public final static String TERMINOLOGY_TX_NOVALID_10 = "Terminology_TX_NoValid_10"; + public final static String TERMINOLOGY_TX_NOVALID_8 = "Terminology_TX_NoValid_8"; + public final static String TERMINOLOGY_TX_NOVALID_7 = "Terminology_TX_NoValid_7"; + public final static String TERMINOLOGY_TX_NOVALID_6 = "Terminology_TX_NoValid_6"; + public final static String TERMINOLOGY_TX_NOVALID_5 = "Terminology_TX_NoValid_5"; + public final static String TERMINOLOGY_TX_NOVALID_4 = "Terminology_TX_NoValid_4"; + public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT = "Terminology_TX_Error_CodeableConcept"; + public final static String TERMINOLOGY_TX_CODE_NOTVALID = "Terminology_TX_Code_NotValid"; + public final static String TERMINOLOGY_TX_NOVALID_3 = "Terminology_TX_NoValid_3"; + public final static String TERMINOLOGY_TX_NOVALID_2 = "Terminology_TX_NoValid_2"; + public final static String TERMINOLOGY_TX_NOVALID_1 = "Terminology_TX_NoValid_1"; + public final static String TERMINOLOGY_TX_CONFIRM_3 = "Terminology_TX_Confirm_3"; + public final static String TERMINOLOGY_TX_CONFIRM_2 = "Terminology_TX_Confirm_2"; + public final static String TERMINOLOGY_TX_CONFIRM_1 = "Terminology_TX_Confirm_1"; + public final static String TERMINOLOGY_TX_CODE_VALUESET_EXT = "Terminology_TX_Code_ValueSet_Ext"; + public final static String TERMINOLOGY_TX_CODE_VALUESETMAX = "Terminology_TX_Code_ValueSetMax"; + public final static String TERMINOLOGY_TX_BINDING_MISSING = "Terminology_TX_Binding_Missing"; + public final static String TERMINOLOGY_TX_CODE_VALUESET = "Terminology_TX_Code_ValueSet"; + public final static String TERMINOLOGY_TX_CODING_COUNT = "Terminology_TX_Coding_Count"; + public final static String TERMINOLOGY_TX_SYSTEM_NOTKNOWN = "Terminology_TX_System_NotKnown"; + public final static String TERMINOLOGY_TX_SYSTEM_VALUESET = "Terminology_TX_System_ValueSet"; + public final static String TERMINOLOGY_TX_SYSTEM_INVALID = "Terminology_TX_System_Invalid"; + public final static String TERMINOLOGY_TX_DISPLAY_WRONG = "Terminology_TX_Display_Wrong"; + public final static String TERMINOLOGY_TX_CODE_UNKNOWN = "Terminology_TX_Code_Unknown"; + public final static String TERMINOLOGY_TX_SYSTEM_UNKNOWN = "Terminology_TX_System_Unknown"; + public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; + public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; + public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; +} diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 19ba4a1e4..a8999d262 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -1,230 +1,226 @@ -The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Expected__but_found__line_elements = Expected {0} but found {1} line elements -_for_ = {0} for '{1}#{2}' -Unknown_Code_System_ = Unknown Code System '{0}' -Unknown_Code_ = Unknown Code ({0}#{1}) -Display_should_be_ = Display should be '{0}' -Invalid_System_URI_ = Invalid System URI: {0} -Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system = Invalid System URI: {0} - cannot use a value set URI as a system -Code_System_URI__is_unknown_so_the_code_cannot_be_validated = Code System URI '{0}' is unknown so the code cannot be validated -Expected__but_found__coding_elements = Expected {0} but found {1} coding elements -Binding_for__missing_cc = Binding for {0} missing (cc) -ValueSet__not_found_by_validator = ValueSet {0} not found by validator -No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) -No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_ = No code provided, and a code must be provided from the value set {0} (max value set {1}) -No_code_provided_and_a_code_should_be_provided_from_the_value_set__ = No code provided, and a code should be provided from the value set {0} ({1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__ = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__ = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__ = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__ = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__ = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) -Error__validating_CodeableConcept = Error {0} validating CodeableConcept -Binding_by_URI_reference_cannot_be_checked = Binding by URI reference cannot be checked -Binding_for_path__has_no_source_so_cant_be_checked = Binding for path {0} has no source, so can't be checked -Code_0_is_not_a_valid_code_in_code_system_1 = Code {0} is not a valid code in code system {1} -Codingsystem_must_be_an_absolute_reference_not_a_local_reference = Coding.system must be an absolute reference, not a local reference -The_Coding_references_a_value_set_not_a_code_system_ = The Coding references a value set, not a code system ('{0}') -Binding_for__missing = Binding for {0} missing -The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided could not be validated in the absence of a terminology server -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set = The Coding provided is not in the value set {0}, and a code is required from this value set{1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} -Error__validating_Coding = Error {0} validating Coding -Error__validating_Coding_ = Error {0} validating Coding: {1} -None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__ = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) -None_of_the_codes_provided_are_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Error__validating_CodeableConcept_using_maxValueSet = Error {0} validating CodeableConcept using maxValueSet -The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__ = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) -The_code_provided_is_not_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_code__ = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) -The_code_provided_is_not_in_the_maximum_value_set__ = The code provided is not in the maximum value set {0} ({1}{2} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_ = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Extension_url__is_not_valid_invalidVersion = Extension url '{0}' is not valid (invalidVersion'{1}') -Extension_url__is_not_valid_unknown_Element_id = Extension url '{0}' is not valid (unknown Element id'{1}') -Extension_url__is_not_valid_Element_id_is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id'{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) -Extension_url__evaluation_state_illegal = Extension url '{0}' evaluation state illegal -Subextension_url__is_not_defined_by_the_Extension_ = Sub-extension url '{0}' is not defined by the Extension {1} -The_extension__is_unknown_and_not_allowed_here = The extension {0} is unknown, and not allowed here -Unknown_extension_ = Unknown extension {0} -Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is -The_Extension__must_be_used_as_a_modifierExtension = The Extension '{0}' must be used as a modifierExtension -The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -The_Extension__definition_allows_for_the_types__but_found_type_ = The Extension '{0}' definition allows for the types {1} but found type {2} -The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_ = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) -The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_ = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') -The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile -Missing_element___required_by_fixed_value_assigned_in_profile_ = Missing element '{0}' - required by fixed value assigned in profile {1} -Value_is__but_must_be_ = Value is '{0}' but must be '{1}' -Unhandled_fixed_value_type_ = Unhandled fixed value type {0} -No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions = No extensions allowed, as the specified fixed value doesn't contain any extensions -Extensions_count_mismatch_expected__but_found_ = Extensions count mismatch: expected {0} but found {1} -Extension_count_mismatch_unable_to_find_extension_ = Extension count mismatch: unable to find extension: {0} -Expected__but_found__family_elements = Expected {0} but found {1} family elements -Expected__but_found__given_elements = Expected {0} but found {1} given elements -Expected__but_found__prefix_elements = Expected {0} but found {1} prefix elements -Expected__but_found__suffix_elements = Expected {0} but found {1} suffix elements -Identifiersystem_must_be_an_absolute_reference_not_a_local_reference = Identifier.system must be an absolute reference, not a local reference -Primitive_types_must_have_a_value_or_must_have_child_extensions = Primitive types must have a value or must have child extensions -Primitive_types_must_have_a_value_that_is_not_empty = Primitive types must have a value that is not empty -Primitive_types_should_not_only_be_whitespace = Primitive types should not only be whitespace -Element_value__does_not_meet_regex_ = Element value '{0}' does not meet regex '{1}' -boolean_values_must_be_true_or_false = boolean values must be 'true' or 'false' -URI_values_cannot_start_with_oid = URI values cannot start with oid: -URI_values_cannot_start_with_uuid = URI values cannot start with uuid: -URI_values_cannot_have_whitespace = URI values cannot have whitespace('{0}') -value_is_longer_than_permitted_maximum_length_of_ = value is longer than permitted maximum length of {0} -OIDs_must_start_with_urnoid = OIDs must start with urn:oid: -OIDs_must_be_valid = OIDs must be valid -UUIDs_must_start_with_urnuuid = UUIDs must start with urn:uuid: -UUIDs_must_be_valid_ = UUIDs must be valid ({0}) -URL_value__does_not_resolve = URL value '{0}' does not resolve -id_value__is_not_valid = id value '{0}' is not valid -value_cannot_be_empty = @value cannot be empty -value_should_not_start_or_finish_with_whitespace = value should not start or finish with whitespace -value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes = value is longer than permitted maximum length of 1 MB (1048576 bytes) -The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error = The value '{0}' is outside the range of reasonable years - check for data entry error -Not_a_valid_date_time = Not a valid date time -if_a_date_has_a_time_it_must_have_a_timezone = if a date has a time, it must have a timezone -Not_a_valid_datetime_ = Not a valid date/time ({0}) -Not_a_valid_time = Not a valid time -Not_a_valid_time_ = Not a valid time ({0}) -Not_a_valid_date = Not a valid date -value_is_longer_than_permitted_maximum_value_of_ = value is longer than permitted maximum value of {0} -Not_a_valid_date_ = Not a valid date ({0}) -The_value0_is_not_a_valid_Base64_value = The value'{0}' is not a valid Base64 value -The_value__is_not_a_valid_integer = The value '{0}' is not a valid integer -value_is_greater_than_permitted_maximum_value_of_ = value is greater than permitted maximum value of {0} -value_is_less_than_permitted_minimum_value_of_ = value is less than permitted minimum value of {0} -value_is_less_than_permitted_minimum_value_of_0 = value is less than permitted minimum value of 0 -value_is_less_than_permitted_minimum_value_of_1 = value is less than permitted minimum value of 1 -The_value__is_not_a_valid_integer64 = The value '{0}' is not a valid integer64 -The_value__is_not_a_valid_decimal = The value '{0}' is not a valid decimal -The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals = The value '{0}' is outside the range of commonly/reasonably supported decimals -The_instant__is_not_valid_by_regex = The instant '{0}' is not valid (by regex) -Not_a_valid_instant_ = Not a valid instant ({0}) -The_code__is_not_valid_whitespace_rules = The code '{0}' is not valid (whitespace rules) -Wrong_namespace_on_the_XHTML__should_be_ = Wrong namespace on the XHTML ('{0}', should be '{1}') -Wrong_name_on_the_XHTML___must_start_with_div = Wrong name on the XHTML ('{0}') - must start with div -Illegal_element_name_in_the_XHTML_ = Illegal element name in the XHTML ('{0}') -Illegal_attribute_name_in_the_XHTML__on_ = Illegal attribute name in the XHTML ('{0}' on '{1}') -ValueSet_0_not_found_by_validator = ValueSet {0} not found by validator -The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided ('{0}') could not be validated in the absence of a terminology server -The_value_provided__is_not_in_the_value_set___and_a_code_is_required_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} -Binding_has_no_source_so_cant_be_checked = Binding has no source, so can't be checked -A_Reference_without_an_actual_reference_or_identifier_should_have_a_display = A Reference without an actual reference or identifier should have a display -Unable_to_resolve_resource_ = Unable to resolve resource '{0}' -The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' is not a valid Target for this element (must be one of {1}) -The_specified_type__does_not_match_the_found_type_ = The specified type '{0}' does not match the found type '{1}' -Unable_to_determine_type_of_target_resource = Unable to determine type of target resource -Unable_to_resolve_the_profile_reference_ = Unable to resolve the profile reference '{0}' -Unable_to_find_matching_profile_for__by_type_among_choices_ = Unable to find matching profile for {0} (by type) among choices: {1} -Unable_to_find_matching_profile_for__among_choices_ = Unable to find matching profile for {0} among choices: {1} -Found_multiple_matching_profiles_for__among_choices_ = Found multiple matching profiles for {0} among choices: {1} -Invalid_Resource_target_type_Found__but_expected_one_of_ = Invalid Resource target type. Found {0}, but expected one of ({1}) -Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference -Bundled_or_contained_reference_not_found_within_the_bundleresource_ = Bundled or contained reference not found within the bundle/resource {0} -The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' implied by the reference URL {1} is not a valid Target for this element (must be one of {2}) -Expected__but_found__event_elements = Expected {0} but found {1} event elements -Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl = Relative Reference appears inside Bundle whose entry is missing a fullUrl -Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_ = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) -Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_ = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} -Multiple_matches_in_bundle_for_reference_ = Multiple matches in bundle for reference {0} -Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references -Matching_reference_for_reference__has_resourceType_ = Matching reference for reference {0} has resourceType {1} -URN_reference_is_not_locally_contained_within_the_bundle_ = URN reference is not locally contained within the bundle {0} -Bundle_entry_missing_fullUrl = Bundle entry missing fullUrl -Profile_reference__could_not_be_resolved_so_has_not_been_checked = Profile reference '{0}' could not be resolved, so has not been checked -StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided -Questions_with_an_enableWhen_must_have_a_value_for_the_question_link = Questions with an enableWhen must have a value for the question link -Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition -Unable_to_find_target__for_this_question_enableWhen = Unable to find target '{0}' for this question enableWhen -Target_for_this_question_enableWhen_cant_reference_itself = Target for this question enableWhen can't reference itself -The_target_of_this_enableWhen_rule__comes_after_the_question_itself = The target of this enableWhen rule ({0}) comes after the question itself -Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_ = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ -Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_ = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ -Duplicate_Security_Label_ = Duplicate Security Label {0} -Type_mismatch__SearchParameter__type_is__but_type_here_is_ = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} -CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_ = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) -CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire = No questionnaire is identified, so no validation can be performed against the base questionnaire -The_questionnaire_could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire'{0}' could not be resolved, so no validation can be performed against the base questionnaire -If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_ = If text exists, it must match the questionnaire definition for linkId {0} -No_response_answer_found_for_required_item_ = No response answer found for required item {0} -Item_has_answer_2_even_though_it_is_not_enabled_ = Item has answer (2), even though it is not enabled {0} -Only_one_response_answer_item_with_this_linkId_allowed = Only one response answer item with this linkId allowed -Items_of_type_group_should_not_have_answers = Items of type group should not have answers -Definition_for_item__does_not_contain_a_type = Definition for item {0} does not contain a type -Items_not_of_type_DISPLAY_should_not_have_items__linkId_0 = Items not of type DISPLAY should not have items - linkId {0} -Only_one_response_item_with_this_linkId_allowed__ = Only one response item with this linkId allowed - {0} -No_LinkId_so_cant_be_validated = No LinkId, so can't be validated -LinkId_not_found_in_questionnaire = LinkId'{0}' not found in questionnaire -Structural_Error_items_are_out_of_order = Structural Error: items are out of order -Item_has_answer_even_though_it_is_not_enabled_item_id__ = Item has answer, even though it is not enabled (item id = '{0}') -Answer_value_must_be_of_type_ = Answer value must be of type {0} -Answer_value_must_be_one_of_the_types_ = Answer value must be one of the types {0} -The_value_provided__is_not_in_the_options_value_set_in_the_questionnaire = The value provided ({0}::{1}) is not in the options value set in the questionnaire -Error__validating_Coding_against_Questionnaire_Options = Error {0} validating Coding against Questionnaire Options -Cannot_validate_options_because_no_option_or_options_are_provided = Cannot validate options because no option or options are provided -Option_list_has_no_option_values_of_type_integer = Option list has no option values of type integer -The_integer__is_not_a_valid_option = The integer {0} is not a valid option -Cannot_validate_integer_answer_option_because_no_option_list_is_provided = Cannot validate integer answer option because no option list is provided -Option_list_has_no_option_values_of_type_date = Option list has no option values of type date -The_date__is_not_a_valid_option = The date {0} is not a valid option -Cannot_validate_date_answer_option_because_no_option_list_is_provided = Cannot validate date answer option because no option list is provided -Option_list_has_no_option_values_of_type_time = Option list has no option values of type time -The_time__is_not_a_valid_option = The time {0} is not a valid option -Cannot_validate_time_answer_option_because_no_option_list_is_provided = Cannot validate time answer option because no option list is provided -Option_list_has_no_option_values_of_type_string = Option list has no option values of type string -The_string__is_not_a_valid_option = The string {0} is not a valid option -Cannot_validate_string_answer_option_because_no_option_list_is_provided = Cannot validate string answer option because no option list is provided -Option_list_has_no_option_values_of_type_coding = Option list has no option values of type coding -The_code__is_not_a_valid_option = The code {0}::{1} is not a valid option -Cannot_validate_Coding_option_because_no_option_list_is_provided = Cannot validate Coding option because no option list is provided -Documents_or_Messages_must_contain_at_least_one_entry = Documents or Messages must contain at least one entry -No_resource_on_first_entry = No resource on first entry -The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches -The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself -Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__ = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') -Entry__isnt_reachable_by_traversing_from_first_Bundle_entry = Entry {0} isn't reachable by traversing from first Bundle entry -Cant_find__in_the_bundle_ = Can't find '{0}' in the bundle ({1}) -The_type__is_not_valid__no_resources_allowed_here = The type '{0} is not valid - no resources allowed here -No_profile_found_for_contained_resource_of_type_ = No profile found for contained resource of type '{0}' -The_type__is_not_valid__must_be_ = The type '{0}' is not valid - must be {1} -The_type__is_not_valid__must_be_one_of_ = The type '{0}' is not valid - must be one of {1} -The_first_entry_in_a_document_must_be_a_composition = The first entry in a document must be a composition -The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_ = The type of element {0} is not known, which is illegal. Valid types at this point are {1} -Extensionurl_is_required = Extension.url is required -Extensionurl_must_be_an_absolute_URL = Extension.url must be an absolute URL -Unrecognised_Content_ = Unrecognised Content {0} -Unknown_type_ = Unknown type {0} -Unknown_profile_ = Unknown profile {0} -Unable_to_find_matching_profile_among_choices_ = Unable to find matching profile among choices: {0} -Found_multiple_matching_profiles_among_choices_ = Found multiple matching profiles among choices: {0} -_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -_minimum_required___but_only_found_ = {0}: minimum required = {1}, but only found {2} -_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -_max_allowed___but_found_ = {0}: max allowed = {1}, but found {2} -This_element_does_not_match_any_known_slice_ = This element does not match any known slice {0} -This_element_is_not_allowed_by_the_profile_ = This element is not allowed by the profile {0} -As_specified_by_profile__Element__is_out_of_order = As specified by profile {0}, Element '{1}' is out of order -As_specified_by_profile__Element__is_out_of_order_in_ordered_slice = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Profile__Element_matches_more_than_one_slice___ = Profile {0}, Element matches more than one slice - {1}, {2} -The_first_entry_in_a_message_must_be_a_MessageHeader = The first entry in a message must be a MessageHeader -No_definition_found_for_resource_type_ = No definition found for resource type '{0}' -Specified_profile_type_was__but_found_type_ = Specified profile type was '{0}', but found type '{1}' -Resource_requires_an_id_but_none_is_present = Resource requires an id, but none is present -Resource_has_an_id_but_none_is_allowed = Resource has an id, but none is allowed +#InstanceValidator +Resource_RES_ID_Prohibited = Resource has an id, but none is allowed +Resource_RES_ID_Missing = Resource requires an id, but none is present +Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' +Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader +Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order +Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0} +Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} +Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} +Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} +Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} +Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} +Validation_VAL_Unknown_Profile = Unknown profile {0} +Validation_VAL_NoType = Unknown type {0} +Validation_VAL_Content_Unknown = Unrecognised Content {0} +Extension_EXT_URL_Absolute = Extension.url must be an absolute URL +Extension_EXT_Url_NotFound = Extension.url is required +Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition +Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' +Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here +Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) +Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself +Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry +Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry +Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided +Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option +Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding +Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided +Questionnaire_QR_Item_NoString = The string {0} is not a valid option +Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string +Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided +Questionnaire_QR_Item_NoTime = The time {0} is not a valid option +Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time +Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided +Questionnaire_QR_Item_NoDate = The date {0} is not a valid option +Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date +Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided +Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option +Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer +Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided +Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options +Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire +Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator +Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} +Questionnaire_QR_Item_WrongType = Answer value must be of type {0} +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') +Questionnaire_QR_Item_Order = Structural Error: items are out of order +Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} +Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} +Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type +Questionnaire_QR_Item_Group = Items of type group should not have answers +Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed +Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} +Questionnaire_QR_Item_Missing = No response answer found for required item {0} +Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} +Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +Meta_RES_Security_Duplicate = Duplicate Security Label {0} +Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link +Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided +Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked +Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl +Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} +Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references +Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} +Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) +Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Bundle_MSG_Event_Count = Expected {0} but found {1} event elements +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_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} +Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1}) +Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} +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_CantResolveProfile = Unable to resolve the profile reference '{0}' +Reference_REF_NoType = Unable to determine type of target resource +Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Reference_REF_CantResolve = Unable to resolve resource '{0}' +Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display +Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server +Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div +Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} +Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) +Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) +Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal +Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 +Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 +Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} +Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} +Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) +Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) +Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) +Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time +Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) +Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace +Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty +Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) +Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: +Type_Specific_Checks_DT_OID_Valid = OIDs must be valid +Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') +Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: +Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: +Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace +Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions +Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference +Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements +Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements +Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements +Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements +Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} +Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions +Internal_INT_Bad_Type = Unhandled fixed value type {0} +_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' +Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) +Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is +Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_EXT_Unknown = Unknown extension {0} +Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here +Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal +Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') +Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') +Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked +Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked +Terminology_TX_Error_Coding1 = Error {0} validating Coding +Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} +Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server +Terminology_TX_Binding_Missing2 = Binding for {0} missing +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference +Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet +Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} +Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} +Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept +Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} +Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) +Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) +Terminology_TX_Binding_Missing = Binding for {0} missing (cc) +Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) +Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements +Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system +Terminology_TX_System_Invalid = Invalid System URI: {0} +Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) +Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements +MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index 19ba4a1e4..a8999d262 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -1,230 +1,226 @@ -The_element__is_not_marked_as_mustSupport_in_the_profile__Consider_not_using_the_element_or_marking_the_element_as_mustSupport_in_the_profile = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Expected__but_found__line_elements = Expected {0} but found {1} line elements -_for_ = {0} for '{1}#{2}' -Unknown_Code_System_ = Unknown Code System '{0}' -Unknown_Code_ = Unknown Code ({0}#{1}) -Display_should_be_ = Display should be '{0}' -Invalid_System_URI_ = Invalid System URI: {0} -Invalid_System_URI___cannot_use_a_value_set_URI_as_a_system = Invalid System URI: {0} - cannot use a value set URI as a system -Code_System_URI__is_unknown_so_the_code_cannot_be_validated = Code System URI '{0}' is unknown so the code cannot be validated -Expected__but_found__coding_elements = Expected {0} but found {1} coding elements -Binding_for__missing_cc = Binding for {0} missing (cc) -ValueSet__not_found_by_validator = ValueSet {0} not found by validator -No_code_provided_and_a_code_is_required_from_the_value_set__ = No code provided, and a code is required from the value set {0} ({1}) -No_code_provided_and_a_code_must_be_provided_from_the_value_set__max_value_set_ = No code provided, and a code must be provided from the value set {0} (max value set {1}) -No_code_provided_and_a_code_should_be_provided_from_the_value_set__ = No code provided, and a code should be provided from the value set {0} ({1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required_class__ = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_class__ = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_class__ = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_codes__ = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) -None_of_the_codes_provided_are_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set_codes__ = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) -Error__validating_CodeableConcept = Error {0} validating CodeableConcept -Binding_by_URI_reference_cannot_be_checked = Binding by URI reference cannot be checked -Binding_for_path__has_no_source_so_cant_be_checked = Binding for path {0} has no source, so can't be checked -Code_0_is_not_a_valid_code_in_code_system_1 = Code {0} is not a valid code in code system {1} -Codingsystem_must_be_an_absolute_reference_not_a_local_reference = Coding.system must be an absolute reference, not a local reference -The_Coding_references_a_value_set_not_a_code_system_ = The Coding references a value set, not a code system ('{0}') -Binding_for__missing = Binding for {0} missing -The_value_provided_could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided could not be validated in the absence of a terminology server -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_from_this_value_set_is_required = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code -Could_not_confirm_that_the_codes_provided_are_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set = The Coding provided is not in the value set {0}, and a code is required from this value set{1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} -Error__validating_Coding = Error {0} validating Coding -Error__validating_Coding_ = Error {0} validating Coding: {1} -None_of_the_codes_provided_could_be_validated_against_the_maximum_value_set___error__ = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) -None_of_the_codes_provided_are_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_codes__ = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Error__validating_CodeableConcept_using_maxValueSet = Error {0} validating CodeableConcept using maxValueSet -The_code_provided_could_not_be_validated_against_the_maximum_value_set___error__ = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) -The_code_provided_is_not_in_the_maximum_value_set___and_a_code_from_this_value_set_is_required_code__ = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) -The_code_provided_is_not_in_the_maximum_value_set__ = The code provided is not in the maximum value set {0} ({1}{2} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_required_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code_ = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} -The_Coding_provided_is_not_in_the_value_set__and_a_code_is_recommended_to_come_from_this_value_set_ = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Extension_url__is_not_valid_invalidVersion = Extension url '{0}' is not valid (invalidVersion'{1}') -Extension_url__is_not_valid_unknown_Element_id = Extension url '{0}' is not valid (unknown Element id'{1}') -Extension_url__is_not_valid_Element_id_is_valid_but_cannot_be_used_in_a_crossversion_paradigm_because_there_has_been_no_changes_across_the_relevant_versions = Extension url '{0}' is not valid (Element id'{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) -Extension_url__evaluation_state_illegal = Extension url '{0}' evaluation state illegal -Subextension_url__is_not_defined_by_the_Extension_ = Sub-extension url '{0}' is not defined by the Extension {1} -The_extension__is_unknown_and_not_allowed_here = The extension {0} is unknown, and not allowed here -Unknown_extension_ = Unknown extension {0} -Extension_modifier_mismatch_the_extension_element_is_labelled_as_a_modifier_but_the_underlying_extension_is_not = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_modifier_mismatch_the_extension_element_is_not_labelled_as_a_modifier_but_the_underlying_extension_is = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is -The_Extension__must_be_used_as_a_modifierExtension = The Extension '{0}' must be used as a modifierExtension -The_Extension__must_not_be_used_as_an_extension_its_a_modifierExtension = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -The_Extension__definition_is_for_a_simple_extension_so_it_must_contain_a_value_not_extensions = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -The_Extension__definition_allows_for_the_types__but_found_type_ = The Extension '{0}' definition allows for the types {1} but found type {2} -The_extension__is_not_allowed_to_be_used_at_this_point_allowed___this_element_is_ = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) -The_extension__is_not_allowed_to_be_used_at_this_point_based_on_context_invariant_ = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') -The_element__is_present_in_the_instance_but_not_allowed_in_the_applicable__specified_in_profile = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile -Missing_element___required_by_fixed_value_assigned_in_profile_ = Missing element '{0}' - required by fixed value assigned in profile {1} -Value_is__but_must_be_ = Value is '{0}' but must be '{1}' -Unhandled_fixed_value_type_ = Unhandled fixed value type {0} -No_extensions_allowed_as_the_specified_fixed_value_doesnt_contain_any_extensions = No extensions allowed, as the specified fixed value doesn't contain any extensions -Extensions_count_mismatch_expected__but_found_ = Extensions count mismatch: expected {0} but found {1} -Extension_count_mismatch_unable_to_find_extension_ = Extension count mismatch: unable to find extension: {0} -Expected__but_found__family_elements = Expected {0} but found {1} family elements -Expected__but_found__given_elements = Expected {0} but found {1} given elements -Expected__but_found__prefix_elements = Expected {0} but found {1} prefix elements -Expected__but_found__suffix_elements = Expected {0} but found {1} suffix elements -Identifiersystem_must_be_an_absolute_reference_not_a_local_reference = Identifier.system must be an absolute reference, not a local reference -Primitive_types_must_have_a_value_or_must_have_child_extensions = Primitive types must have a value or must have child extensions -Primitive_types_must_have_a_value_that_is_not_empty = Primitive types must have a value that is not empty -Primitive_types_should_not_only_be_whitespace = Primitive types should not only be whitespace -Element_value__does_not_meet_regex_ = Element value '{0}' does not meet regex '{1}' -boolean_values_must_be_true_or_false = boolean values must be 'true' or 'false' -URI_values_cannot_start_with_oid = URI values cannot start with oid: -URI_values_cannot_start_with_uuid = URI values cannot start with uuid: -URI_values_cannot_have_whitespace = URI values cannot have whitespace('{0}') -value_is_longer_than_permitted_maximum_length_of_ = value is longer than permitted maximum length of {0} -OIDs_must_start_with_urnoid = OIDs must start with urn:oid: -OIDs_must_be_valid = OIDs must be valid -UUIDs_must_start_with_urnuuid = UUIDs must start with urn:uuid: -UUIDs_must_be_valid_ = UUIDs must be valid ({0}) -URL_value__does_not_resolve = URL value '{0}' does not resolve -id_value__is_not_valid = id value '{0}' is not valid -value_cannot_be_empty = @value cannot be empty -value_should_not_start_or_finish_with_whitespace = value should not start or finish with whitespace -value_is_longer_than_permitted_maximum_length_of_1_MB_1048576_bytes = value is longer than permitted maximum length of 1 MB (1048576 bytes) -The_value__is_outside_the_range_of_reasonable_years__check_for_data_entry_error = The value '{0}' is outside the range of reasonable years - check for data entry error -Not_a_valid_date_time = Not a valid date time -if_a_date_has_a_time_it_must_have_a_timezone = if a date has a time, it must have a timezone -Not_a_valid_datetime_ = Not a valid date/time ({0}) -Not_a_valid_time = Not a valid time -Not_a_valid_time_ = Not a valid time ({0}) -Not_a_valid_date = Not a valid date -value_is_longer_than_permitted_maximum_value_of_ = value is longer than permitted maximum value of {0} -Not_a_valid_date_ = Not a valid date ({0}) -The_value0_is_not_a_valid_Base64_value = The value'{0}' is not a valid Base64 value -The_value__is_not_a_valid_integer = The value '{0}' is not a valid integer -value_is_greater_than_permitted_maximum_value_of_ = value is greater than permitted maximum value of {0} -value_is_less_than_permitted_minimum_value_of_ = value is less than permitted minimum value of {0} -value_is_less_than_permitted_minimum_value_of_0 = value is less than permitted minimum value of 0 -value_is_less_than_permitted_minimum_value_of_1 = value is less than permitted minimum value of 1 -The_value__is_not_a_valid_integer64 = The value '{0}' is not a valid integer64 -The_value__is_not_a_valid_decimal = The value '{0}' is not a valid decimal -The_value__is_outside_the_range_of_commonlyreasonably_supported_decimals = The value '{0}' is outside the range of commonly/reasonably supported decimals -The_instant__is_not_valid_by_regex = The instant '{0}' is not valid (by regex) -Not_a_valid_instant_ = Not a valid instant ({0}) -The_code__is_not_valid_whitespace_rules = The code '{0}' is not valid (whitespace rules) -Wrong_namespace_on_the_XHTML__should_be_ = Wrong namespace on the XHTML ('{0}', should be '{1}') -Wrong_name_on_the_XHTML___must_start_with_div = Wrong name on the XHTML ('{0}') - must start with div -Illegal_element_name_in_the_XHTML_ = Illegal element name in the XHTML ('{0}') -Illegal_attribute_name_in_the_XHTML__on_ = Illegal attribute name in the XHTML ('{0}' on '{1}') -ValueSet_0_not_found_by_validator = ValueSet {0} not found by validator -The_value_provided__could_not_be_validated_in_the_absence_of_a_terminology_server = The value provided ('{0}') could not be validated in the absence of a terminology server -The_value_provided__is_not_in_the_value_set___and_a_code_is_required_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -The_value_provided__is_not_in_the_value_set___and_a_code_should_come_from_this_value_set_unless_it_has_no_suitable_code = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -The_value_provided__is_not_in_the_value_set___and_a_code_is_recommended_to_come_from_this_value_set = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} -Binding_has_no_source_so_cant_be_checked = Binding has no source, so can't be checked -A_Reference_without_an_actual_reference_or_identifier_should_have_a_display = A Reference without an actual reference or identifier should have a display -Unable_to_resolve_resource_ = Unable to resolve resource '{0}' -The_type__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' is not a valid Target for this element (must be one of {1}) -The_specified_type__does_not_match_the_found_type_ = The specified type '{0}' does not match the found type '{1}' -Unable_to_determine_type_of_target_resource = Unable to determine type of target resource -Unable_to_resolve_the_profile_reference_ = Unable to resolve the profile reference '{0}' -Unable_to_find_matching_profile_for__by_type_among_choices_ = Unable to find matching profile for {0} (by type) among choices: {1} -Unable_to_find_matching_profile_for__among_choices_ = Unable to find matching profile for {0} among choices: {1} -Found_multiple_matching_profiles_for__among_choices_ = Found multiple matching profiles for {0} among choices: {1} -Invalid_Resource_target_type_Found__but_expected_one_of_ = Invalid Resource target type. Found {0}, but expected one of ({1}) -Reference_is__which_isnt_supported_by_the_specified_aggregation_modes_for_the_reference = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference -Bundled_or_contained_reference_not_found_within_the_bundleresource_ = Bundled or contained reference not found within the bundle/resource {0} -The_type__implied_by_the_reference_URL__is_not_a_valid_Target_for_this_element_must_be_one_of_ = The type '{0}' implied by the reference URL {1} is not a valid Target for this element (must be one of {2}) -Expected__but_found__event_elements = Expected {0} but found {1} event elements -Relative_Reference_appears_inside_Bundle_whose_entry_is_missing_a_fullUrl = Relative Reference appears inside Bundle whose entry is missing a fullUrl -Relative_URLs_must_be_of_the_format_ResourceNameid_or_a_search_ULR_is_allowed_typeparameters__Encountered_ = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) -Relative_URLs_must_be_of_the_format_ResourceNameid__Encountered_ = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} -Multiple_matches_in_bundle_for_reference_ = Multiple matches in bundle for reference {0} -Entries_matching_fullURL__should_declare_metaversionId_because_there_are_versionspecific_references = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references -Matching_reference_for_reference__has_resourceType_ = Matching reference for reference {0} has resourceType {1} -URN_reference_is_not_locally_contained_within_the_bundle_ = URN reference is not locally contained within the bundle {0} -Bundle_entry_missing_fullUrl = Bundle entry missing fullUrl -Profile_reference__could_not_be_resolved_so_has_not_been_checked = Profile reference '{0}' could not be resolved, so has not been checked -StructureDefinition_has_no_snapshot__validation_is_against_the_snapshot_so_it_must_be_provided = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided -Questions_with_an_enableWhen_must_have_a_value_for_the_question_link = Questions with an enableWhen must have a value for the question link -Questions_with_an_enableWhen_cannot_refer_to_an_inner_question_for_its_enableWhen_condition = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition -Unable_to_find_target__for_this_question_enableWhen = Unable to find target '{0}' for this question enableWhen -Target_for_this_question_enableWhen_cant_reference_itself = Target for this question enableWhen can't reference itself -The_target_of_this_enableWhen_rule__comes_after_the_question_itself = The target of this enableWhen rule ({0}) comes after the question itself -Resource_has_a_language_but_the_XHTML_does_not_have_an_lang_or_an_xmllang_tag_needs_both__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language_but_the_XHTML_does_not_have_a_lang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language__and_the_XHTML_has_a_lang__but_they_differ_ = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ -Resource_has_a_language_but_the_XHTML_does_not_have_an_xmllang_tag_needs_both_lang_and_xmllang__see_httpswwww3orgTRi18nhtmltechlanglangvalues = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Resource_has_a_language__and_the_XHTML_has_an_xmllang__but_they_differ_ = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ -Duplicate_Security_Label_ = Duplicate Security Label {0} -Type_mismatch__SearchParameter__type_is__but_type_here_is_ = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} -CodeSystem__has_a_all_system_value_set_of__but_it_is_an_expansion = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_single_include = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem__has_a_all_system_value_set_of__but_doesnt_have_a_matching_system_ = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) -CodeSystem__has_a_all_system_value_set_of__but_the_include_has_extra_details = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -No_questionnaire_is_identified_so_no_validation_can_be_performed_against_the_base_questionnaire = No questionnaire is identified, so no validation can be performed against the base questionnaire -The_questionnaire_could_not_be_resolved_so_no_validation_can_be_performed_against_the_base_questionnaire = The questionnaire'{0}' could not be resolved, so no validation can be performed against the base questionnaire -If_text_exists_it_must_match_the_questionnaire_definition_for_linkId_ = If text exists, it must match the questionnaire definition for linkId {0} -No_response_answer_found_for_required_item_ = No response answer found for required item {0} -Item_has_answer_2_even_though_it_is_not_enabled_ = Item has answer (2), even though it is not enabled {0} -Only_one_response_answer_item_with_this_linkId_allowed = Only one response answer item with this linkId allowed -Items_of_type_group_should_not_have_answers = Items of type group should not have answers -Definition_for_item__does_not_contain_a_type = Definition for item {0} does not contain a type -Items_not_of_type_DISPLAY_should_not_have_items__linkId_0 = Items not of type DISPLAY should not have items - linkId {0} -Only_one_response_item_with_this_linkId_allowed__ = Only one response item with this linkId allowed - {0} -No_LinkId_so_cant_be_validated = No LinkId, so can't be validated -LinkId_not_found_in_questionnaire = LinkId'{0}' not found in questionnaire -Structural_Error_items_are_out_of_order = Structural Error: items are out of order -Item_has_answer_even_though_it_is_not_enabled_item_id__ = Item has answer, even though it is not enabled (item id = '{0}') -Answer_value_must_be_of_type_ = Answer value must be of type {0} -Answer_value_must_be_one_of_the_types_ = Answer value must be one of the types {0} -The_value_provided__is_not_in_the_options_value_set_in_the_questionnaire = The value provided ({0}::{1}) is not in the options value set in the questionnaire -Error__validating_Coding_against_Questionnaire_Options = Error {0} validating Coding against Questionnaire Options -Cannot_validate_options_because_no_option_or_options_are_provided = Cannot validate options because no option or options are provided -Option_list_has_no_option_values_of_type_integer = Option list has no option values of type integer -The_integer__is_not_a_valid_option = The integer {0} is not a valid option -Cannot_validate_integer_answer_option_because_no_option_list_is_provided = Cannot validate integer answer option because no option list is provided -Option_list_has_no_option_values_of_type_date = Option list has no option values of type date -The_date__is_not_a_valid_option = The date {0} is not a valid option -Cannot_validate_date_answer_option_because_no_option_list_is_provided = Cannot validate date answer option because no option list is provided -Option_list_has_no_option_values_of_type_time = Option list has no option values of type time -The_time__is_not_a_valid_option = The time {0} is not a valid option -Cannot_validate_time_answer_option_because_no_option_list_is_provided = Cannot validate time answer option because no option list is provided -Option_list_has_no_option_values_of_type_string = Option list has no option values of type string -The_string__is_not_a_valid_option = The string {0} is not a valid option -Cannot_validate_string_answer_option_because_no_option_list_is_provided = Cannot validate string answer option because no option list is provided -Option_list_has_no_option_values_of_type_coding = Option list has no option values of type coding -The_code__is_not_a_valid_option = The code {0}::{1} is not a valid option -Cannot_validate_Coding_option_because_no_option_list_is_provided = Cannot validate Coding option because no option list is provided -Documents_or_Messages_must_contain_at_least_one_entry = Documents or Messages must contain at least one entry -No_resource_on_first_entry = No resource on first entry -The_canonical_URL__cannot_match_the_fullUrl__unless_the_resource_id__also_matches = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches -The_canonical_URL__cannot_match_the_fullUrl__unless_on_the_canonical_server_itself = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself -Resource_ID_does_not_match_the_ID_in_the_entry_full_URL__vs__ = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') -Entry__isnt_reachable_by_traversing_from_first_Bundle_entry = Entry {0} isn't reachable by traversing from first Bundle entry -Cant_find__in_the_bundle_ = Can't find '{0}' in the bundle ({1}) -The_type__is_not_valid__no_resources_allowed_here = The type '{0} is not valid - no resources allowed here -No_profile_found_for_contained_resource_of_type_ = No profile found for contained resource of type '{0}' -The_type__is_not_valid__must_be_ = The type '{0}' is not valid - must be {1} -The_type__is_not_valid__must_be_one_of_ = The type '{0}' is not valid - must be one of {1} -The_first_entry_in_a_document_must_be_a_composition = The first entry in a document must be a composition -The_type_of_element__is_not_known_which_is_illegal_Valid_types_at_this_point_are_ = The type of element {0} is not known, which is illegal. Valid types at this point are {1} -Extensionurl_is_required = Extension.url is required -Extensionurl_must_be_an_absolute_URL = Extension.url must be an absolute URL -Unrecognised_Content_ = Unrecognised Content {0} -Unknown_type_ = Unknown type {0} -Unknown_profile_ = Unknown profile {0} -Unable_to_find_matching_profile_among_choices_ = Unable to find matching profile among choices: {0} -Found_multiple_matching_profiles_among_choices_ = Found multiple matching profiles among choices: {0} -_Unable_to_check_minimum_required__due_to_lack_of_slicing_validation = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -_minimum_required___but_only_found_ = {0}: minimum required = {1}, but only found {2} -_Unable_to_check_max_allowed__due_to_lack_of_slicing_validation = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -_max_allowed___but_found_ = {0}: max allowed = {1}, but found {2} -This_element_does_not_match_any_known_slice_ = This element does not match any known slice {0} -This_element_is_not_allowed_by_the_profile_ = This element is not allowed by the profile {0} -As_specified_by_profile__Element__is_out_of_order = As specified by profile {0}, Element '{1}' is out of order -As_specified_by_profile__Element__is_out_of_order_in_ordered_slice = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Profile__Element_matches_more_than_one_slice___ = Profile {0}, Element matches more than one slice - {1}, {2} -The_first_entry_in_a_message_must_be_a_MessageHeader = The first entry in a message must be a MessageHeader -No_definition_found_for_resource_type_ = No definition found for resource type '{0}' -Specified_profile_type_was__but_found_type_ = Specified profile type was '{0}', but found type '{1}' -Resource_requires_an_id_but_none_is_present = Resource requires an id, but none is present -Resource_has_an_id_but_none_is_allowed = Resource has an id, but none is allowed +#InstanceValidator +Resource_RES_ID_Prohibited = Resource has an id, but none is allowed +Resource_RES_ID_Missing = Resource requires an id, but none is present +Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' +Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader +Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order +Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0} +Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} +Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} +Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} +Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} +Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} +Validation_VAL_Unknown_Profile = Unknown profile {0} +Validation_VAL_NoType = Unknown type {0} +Validation_VAL_Content_Unknown = Unrecognised Content {0} +Extension_EXT_URL_Absolute = Extension.url must be an absolute URL +Extension_EXT_Url_NotFound = Extension.url is required +Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition +Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' +Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here +Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) +Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself +Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry +Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry +Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided +Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option +Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding +Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided +Questionnaire_QR_Item_NoString = The string {0} is not a valid option +Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string +Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided +Questionnaire_QR_Item_NoTime = The time {0} is not a valid option +Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time +Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided +Questionnaire_QR_Item_NoDate = The date {0} is not a valid option +Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date +Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided +Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option +Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer +Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided +Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options +Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire +Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator +Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} +Questionnaire_QR_Item_WrongType = Answer value must be of type {0} +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') +Questionnaire_QR_Item_Order = Structural Error: items are out of order +Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} +Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} +Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type +Questionnaire_QR_Item_Group = Items of type group should not have answers +Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed +Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} +Questionnaire_QR_Item_Missing = No response answer found for required item {0} +Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} +Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +Meta_RES_Security_Duplicate = Duplicate Security Label {0} +Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link +Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided +Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked +Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl +Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} +Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references +Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} +Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) +Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Bundle_MSG_Event_Count = Expected {0} but found {1} event elements +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_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} +Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1}) +Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} +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_CantResolveProfile = Unable to resolve the profile reference '{0}' +Reference_REF_NoType = Unable to determine type of target resource +Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Reference_REF_CantResolve = Unable to resolve resource '{0}' +Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display +Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server +Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div +Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} +Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) +Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) +Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal +Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 +Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 +Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} +Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} +Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) +Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) +Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) +Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time +Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) +Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace +Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty +Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) +Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: +Type_Specific_Checks_DT_OID_Valid = OIDs must be valid +Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') +Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: +Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: +Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace +Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions +Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference +Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements +Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements +Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements +Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements +Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} +Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions +Internal_INT_Bad_Type = Unhandled fixed value type {0} +_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' +Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) +Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is +Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_EXT_Unknown = Unknown extension {0} +Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here +Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal +Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') +Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') +Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked +Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked +Terminology_TX_Error_Coding1 = Error {0} validating Coding +Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} +Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server +Terminology_TX_Binding_Missing2 = Binding for {0} missing +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference +Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet +Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} +Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} +Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept +Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} +Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) +Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) +Terminology_TX_Binding_Missing = Binding for {0} missing (cc) +Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) +Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements +Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system +Terminology_TX_System_Invalid = Invalid System URI: {0} +Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) +Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements +MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile From 3291aaf03ab2ebf399cf2dfaf3befd0b8e6bc11f Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 18:30:42 +0100 Subject: [PATCH 15/52] manually fixed duplicated rule entry message --- .../org/hl7/fhir/validation/instance/InstanceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 0fc1020f4..05290ed97 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -5008,7 +5008,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl()), "This element does not match any known slice" + (profile == null ? "" : " defined in the profile " + profile.getUrl() + ": " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); } else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) { - rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, "This element does not match any known slice " + (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo)), I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicingAsHtml(ei.sliceInfo))); + rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getUrl() + " and slicing is CLOSED: " + errorSummaryForSlicing(ei.sliceInfo))); } } else { // Don't raise this if we're in an abstract profile, like Resource From ef65d09497981bbef747016a5c2f711657bf201d Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 20:28:53 +0100 Subject: [PATCH 16/52] extracted: bpCheck|warningOrError|suppressedwarning|*Exception|Error --- .../instance/InstanceValidator.java | 91 +++++++++---------- .../fhir/validation/utils/I18nConstants.java | 42 +++++++++ .../src/main/resources/Messages.properties | 43 ++++++++- .../main/resources/Messages_de_DE.properties | 43 ++++++++- 4 files changed, 171 insertions(+), 48 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 05290ed97..453cf92f3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -197,17 +197,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override public FunctionDetails resolveFunction(String functionName) { - throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); + throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_, functionName); } @Override public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); + throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION); } @Override public List executeFunction(Object appContext, String functionName, List> parameters) { - throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); + throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION); } @Override @@ -240,7 +240,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat throw new FHIRException(e); } else - throw new Error("Not done yet - resolve " + url + " locally (2)"); + throw new Error( I18nConstants.NOT_DONE_YET__RESOLVE__LOCALLY_2, url); } @@ -269,7 +269,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ValidatorHostContext ctxt = (ValidatorHostContext) appContext; StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); if (sd == null) { - throw new FHIRException("Unable to resolve " + url); + throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_, url); } InstanceValidator self = InstanceValidator.this; List valerrors = new ArrayList(); @@ -285,10 +285,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (e.isResource()) { self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); } else { - throw new FHIRException("Not supported yet"); + throw new FHIRException( I18nConstants.NOT_SUPPORTED_YET); } } else - throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); + throw new NotImplementedException( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT); boolean ok = true; List record = new ArrayList<>(); for (ValidationMessage v : valerrors) { @@ -312,7 +312,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (r instanceof ValueSet) return (ValueSet) r; else - throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); + throw new FHIRException( I18nConstants.REFERENCE__REFERS_TO_A__NOT_A_VALUESET, url, r.fhirType()); } } return null; @@ -513,7 +513,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private StructureDefinition getSpecifiedProfile(String profile) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); if (sd == null) { - throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); + throw new FHIRException( I18nConstants.UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT, profile); } return sd; } @@ -1566,7 +1566,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = true; } } else { - throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); + throw new Error( I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue()); } } if (!ok) { @@ -2092,7 +2092,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (we == null) { if (fetcher == null) { if (!refType.equals("contained")) - throw new FHIRException("Resource resolution services not provided"); + throw new FHIRException( I18nConstants.RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED); } else { Element ext = null; if (fetchCache.containsKey(ref)) { @@ -2477,7 +2477,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); if (sd == null) - throw new DefinitionException("Unable to resolve profile " + p); + throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_PROFILE_, p); profile = sd; if (id == null) element = sd.getSnapshot().getElementFirstRep(); @@ -2488,7 +2488,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat element = t; } if (element == null) - throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); + throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_, id, p); } expr = fpe.parse(fixExpr(discriminator)); t2 = System.nanoTime(); @@ -2639,20 +2639,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (focus.fhirType().equals("Reference") && d.equals("reference")) { String url = focus.getChildValue("reference"); if (Utilities.noString(url)) - throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException( I18nConstants.NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_, discriminator, element.getProperty().getName()); // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? Element target = resolve(appContext, url, stack, errors, p); if (target == null) - throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException( I18nConstants.UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_, url, d, discriminator, element.getProperty().getName()); focus = target; } else if (d.equals("value") && focus.isPrimitive()) { return focus; } else { List children = focus.getChildren(d); if (children.isEmpty()) - throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException( I18nConstants.UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_, d, discriminator, element.getProperty().getName()); if (children.size() > 1) - throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException( I18nConstants.FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_, Integer.toString(children.size()), d, discriminator, element.getProperty().getName()); focus = children.get(0); p = p + "." + d; } @@ -3077,23 +3077,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat discriminator = discriminator.substring(0, discriminator.indexOf('[')); type = criteriaElement.getType().get(0).getWorkingCode(); } else if (criteriaElement.getType().size() > 1) { - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); + throw new DefinitionException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_, discriminator, ed.getId(), profile.getUrl(), criteriaElement.typeSummary()); } else - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); + throw new DefinitionException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getUrl()); if (discriminator.isEmpty()) expression.append(" and $this is " + type); else expression.append(" and " + discriminator + " is " + type); } else if (s.getType() == DiscriminatorType.PROFILE) { if (criteriaElement.getType().size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); } if (criteriaElement.getType().size() != 1) { - throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); } List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); if (list.size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); } else if (list.size() > 1) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); for (CanonicalType c : list) { @@ -3109,7 +3109,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) expression.append(" and (" + discriminator + ".exists().not())"); else - throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); + throw new FHIRException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0, discriminator, ed.getId()); } else if (criteriaElement.hasFixed()) { buildFixedExpression(ed, expression, discriminator, criteriaElement); } else if (criteriaElement.hasPattern()) { @@ -3127,15 +3127,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!anyFound) { if (slicer.getSlicing().getDiscriminator().size() > 1) - throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); + throw new DefinitionException( I18nConstants.COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators); else - throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); + throw new DefinitionException( I18nConstants.COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators); } try { n = fpe.parse(fixExpr(expression.toString())); } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + throw new FHIRException( I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, expression, profile.getUrl(), path, e.getMessage()); } fpeTime = fpeTime + (System.nanoTime() - t); ed.setUserData("slice.expression.cache", n); @@ -3163,7 +3163,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat msg = fpe.forLog(); } catch (Exception ex) { ex.printStackTrace(); - throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); + throw new FHIRException( I18nConstants.PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___, profile.getUrl(), path, n, ex.getMessage()); } return ok; } @@ -3183,13 +3183,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat expression.append(" and "); buildIdentifierExpression(ed, expression, discriminator, ii); } else - throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), pattern.getClass().getName()); } private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) throws DefinitionException { if (ii.hasExtension()) - throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); boolean first = true; expression.append(discriminator + ".where("); if (ii.hasSystem()) { @@ -3223,15 +3223,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) throws DefinitionException { if (cc.hasText()) - throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); if (!cc.hasCoding()) - throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); if (cc.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); boolean firstCoding = true; for (Coding c : cc.getCoding()) { if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); if (firstCoding) firstCoding = false; else expression.append(" and "); expression.append(discriminator + ".coding.where("); @@ -3262,7 +3262,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodingExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Coding c) throws DefinitionException { if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); expression.append(discriminator + ".where("); boolean first = true; if (c.hasSystem()) { @@ -3318,7 +3318,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fixed instanceof BooleanType) { expression.append(((BooleanType) fixed).asStringValue()); } else - throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); + throw new DefinitionException( I18nConstants.UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), fixed.getClass().getName()); expression.append(" in " + discriminator + ")"); } } @@ -3680,7 +3680,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat byte[] json = bs.toByteArray(); switch (v) { case DSTU1: - throw new FHIRException("Unsupported version R1"); + throw new FHIRException( I18nConstants.UNSUPPORTED_VERSION_R1); case DSTU2: org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); Resource r5 = VersionConvertor_10_50.convertResource(r2); @@ -4622,7 +4622,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); if (dt == null) - throw new DefinitionException("Unable to resolve actual type " + actualType); + throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType); trackUsage(dt, hostContext, element); childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); @@ -4980,7 +4980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String errorContext = "profile " + profile.getUrl(); if (!resource.getChildValue("id").isEmpty()) errorContext += "; instance " + resource.getChildValue("id"); - throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); + throw new DefinitionException( I18nConstants.SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___, slicer.getPath(), slicer.getId(), errorContext); } slicer = ed; process = false; @@ -5104,7 +5104,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (tail.equals(t.getId())) return t; } - throw new DefinitionException("Unable to find element with id '" + tail + "'"); + throw new DefinitionException( I18nConstants.UNABLE_TO_FIND_ELEMENT_WITH_ID_, tail); } private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { @@ -5176,7 +5176,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { n = fpe.parse(fixExpr(inv.getExpression())); } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + throw new FHIRException( I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage()); } fpeTime = fpeTime + (System.nanoTime() - t); inv.setUserData("validator.expression.cache", n); @@ -5224,12 +5224,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateObservation(List errors, Element element, NodeStack stack) { // all observations should have a subject, a performer, and a time - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT); List performers = new ArrayList<>(); element.getNamedChildren("performer", performers); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, - "All observations should have an effectiveDateTime or an effectivePeriod"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD); } /* @@ -5316,9 +5315,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); return msgs.size() == 0; } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { - throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE); } else { - throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET); } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java index 0cbc9ded6..be67cee0d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -226,4 +226,46 @@ public class I18nConstants { public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; + public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET = "Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set"; + public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE = "Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD = "All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; + public final static String PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__ = "Problem_processing_expression__in_profile__path__"; + public final static String UNABLE_TO_FIND_ELEMENT_WITH_ID_ = "Unable_to_find_element_with_id_"; + public final static String SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___ = "Slice_encountered_midway_through_set_path___id___"; + public final static String UNABLE_TO_RESOLVE_ACTUAL_TYPE_ = "Unable_to_resolve_actual_type_"; + public final static String UNSUPPORTED_VERSION_R1 = "Unsupported_version_R1"; + public final static String UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_value_type_for_discriminator_for_slice__"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_pattern_type_for_discriminator_for_slice__"; + public final static String PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___ = "Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___"; + public final static String COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions"; + public final static String COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions"; + public final static String DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0 = "Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_only_one_type__in_profile_"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type__in_profile_"; + public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES = "Discriminator__is_based_on_type_but_slice__in__has_no_types"; + public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_ = "Discriminator__is_based_on_type_but_slice__in__has_multiple_types_"; + public final static String FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_ = "Found__items_for__resolving_discriminator__from_"; + public final static String UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find__resolving_discriminator__from_"; + public final static String UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find_resource__at__resolving_discriminator__from_"; + public final static String NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_ = "No_reference_resolving_discriminator__from_"; + public final static String UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_ = "Unable_to_resolve_element__in_profile_"; + public final static String UNABLE_TO_RESOLVE_PROFILE_ = "Unable_to_resolve_profile_"; + public final static String RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED = "Resource_resolution_services_not_provided"; + public final static String UNRECOGNISED_EXTENSION_CONTEXT_ = "Unrecognised_extension_context_"; + public final static String UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT = "Unable_to_locate_the_profile__in_order_to_validate_against_it"; + public final static String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT = "Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element"; + public final static String NOT_SUPPORTED_YET = "Not_supported_yet"; + public final static String UNABLE_TO_RESOLVE_ = "Unable_to_resolve_"; + public final static String NOT_DONE_YET__RESOLVE__LOCALLY_2 = "Not_done_yet__resolve__locally_2"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION = "Not_done_yet_ValidatorHostServicesexecuteFunction"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION = "Not_done_yet_ValidatorHostServicescheckFunction"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_ = "Not_done_yet_ValidatorHostServicesresolveFunction_"; } diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index a8999d262..e72034bb7 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -135,7 +135,6 @@ Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty @@ -224,3 +223,45 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set +Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done +All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod +All_observations_should_have_a_performer = All observations should have a performer +All_observations_should_have_a_subject = All observations should have a subject +Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} +Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} +Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} +Unsupported_version_R1 = Unsupported version R1 +Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} +Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} +Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} +Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} +Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions +Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions +Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 +Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) +Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) +Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) +Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types +Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} +Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} +Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} +Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} +No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} +Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} +Unable_to_resolve_profile_ = Unable to resolve profile {0} +Resource_resolution_services_not_provided = Resource resolution services not provided +Unrecognised_extension_context_ = Unrecognised extension context {0} +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet +Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element +Not_supported_yet = Not supported yet +Unable_to_resolve_ = Unable to resolve {0} +Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) +Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) +Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) +Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index a8999d262..e72034bb7 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -135,7 +135,6 @@ Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty @@ -224,3 +223,45 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set +Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done +All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod +All_observations_should_have_a_performer = All observations should have a performer +All_observations_should_have_a_subject = All observations should have a subject +Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} +Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} +Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} +Unsupported_version_R1 = Unsupported version R1 +Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} +Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} +Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} +Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} +Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions +Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions +Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 +Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) +Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) +Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) +Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types +Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} +Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} +Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} +Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} +No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} +Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} +Unable_to_resolve_profile_ = Unable to resolve profile {0} +Resource_resolution_services_not_provided = Resource resolution services not provided +Unrecognised_extension_context_ = Unrecognised extension context {0} +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet +Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element +Not_supported_yet = Not supported yet +Unable_to_resolve_ = Unable to resolve {0} +Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) +Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) +Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) +Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} From 013dddf2b65a720d2a388477fc12de077b155beb Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 20:34:26 +0100 Subject: [PATCH 17/52] Revert "extracted: bpCheck|warningOrError|suppressedwarning|*Exception|Error" This reverts commit ef65d094 --- .../instance/InstanceValidator.java | 91 ++++++++++--------- .../fhir/validation/utils/I18nConstants.java | 42 --------- .../src/main/resources/Messages.properties | 43 +-------- .../main/resources/Messages_de_DE.properties | 43 +-------- 4 files changed, 48 insertions(+), 171 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 453cf92f3..05290ed97 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -197,17 +197,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override public FunctionDetails resolveFunction(String functionName) { - throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_, functionName); + throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); } @Override public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION); + throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); } @Override public List executeFunction(Object appContext, String functionName, List> parameters) { - throw new Error( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION); + throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); } @Override @@ -240,7 +240,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat throw new FHIRException(e); } else - throw new Error( I18nConstants.NOT_DONE_YET__RESOLVE__LOCALLY_2, url); + throw new Error("Not done yet - resolve " + url + " locally (2)"); } @@ -269,7 +269,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ValidatorHostContext ctxt = (ValidatorHostContext) appContext; StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); if (sd == null) { - throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_, url); + throw new FHIRException("Unable to resolve " + url); } InstanceValidator self = InstanceValidator.this; List valerrors = new ArrayList(); @@ -285,10 +285,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (e.isResource()) { self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); } else { - throw new FHIRException( I18nConstants.NOT_SUPPORTED_YET); + throw new FHIRException("Not supported yet"); } } else - throw new NotImplementedException( I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT); + throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); boolean ok = true; List record = new ArrayList<>(); for (ValidationMessage v : valerrors) { @@ -312,7 +312,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (r instanceof ValueSet) return (ValueSet) r; else - throw new FHIRException( I18nConstants.REFERENCE__REFERS_TO_A__NOT_A_VALUESET, url, r.fhirType()); + throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); } } return null; @@ -513,7 +513,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private StructureDefinition getSpecifiedProfile(String profile) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); if (sd == null) { - throw new FHIRException( I18nConstants.UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT, profile); + throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); } return sd; } @@ -1566,7 +1566,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = true; } } else { - throw new Error( I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue()); + throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); } } if (!ok) { @@ -2092,7 +2092,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (we == null) { if (fetcher == null) { if (!refType.equals("contained")) - throw new FHIRException( I18nConstants.RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED); + throw new FHIRException("Resource resolution services not provided"); } else { Element ext = null; if (fetchCache.containsKey(ref)) { @@ -2477,7 +2477,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); if (sd == null) - throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_PROFILE_, p); + throw new DefinitionException("Unable to resolve profile " + p); profile = sd; if (id == null) element = sd.getSnapshot().getElementFirstRep(); @@ -2488,7 +2488,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat element = t; } if (element == null) - throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_, id, p); + throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); } expr = fpe.parse(fixExpr(discriminator)); t2 = System.nanoTime(); @@ -2639,20 +2639,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (focus.fhirType().equals("Reference") && d.equals("reference")) { String url = focus.getChildValue("reference"); if (Utilities.noString(url)) - throw new FHIRException( I18nConstants.NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_, discriminator, element.getProperty().getName()); + throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? Element target = resolve(appContext, url, stack, errors, p); if (target == null) - throw new FHIRException( I18nConstants.UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_, url, d, discriminator, element.getProperty().getName()); + throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); focus = target; } else if (d.equals("value") && focus.isPrimitive()) { return focus; } else { List children = focus.getChildren(d); if (children.isEmpty()) - throw new FHIRException( I18nConstants.UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_, d, discriminator, element.getProperty().getName()); + throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); if (children.size() > 1) - throw new FHIRException( I18nConstants.FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_, Integer.toString(children.size()), d, discriminator, element.getProperty().getName()); + throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); focus = children.get(0); p = p + "." + d; } @@ -3077,23 +3077,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat discriminator = discriminator.substring(0, discriminator.indexOf('[')); type = criteriaElement.getType().get(0).getWorkingCode(); } else if (criteriaElement.getType().size() > 1) { - throw new DefinitionException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_, discriminator, ed.getId(), profile.getUrl(), criteriaElement.typeSummary()); + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); } else - throw new DefinitionException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getUrl()); + throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); if (discriminator.isEmpty()) expression.append(" and $this is " + type); else expression.append(" and " + discriminator + " is " + type); } else if (s.getType() == DiscriminatorType.PROFILE) { if (criteriaElement.getType().size() == 0) { - throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); + throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); } if (criteriaElement.getType().size() != 1) { - throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); + throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); } List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); if (list.size() == 0) { - throw new DefinitionException( I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()); + throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); } else if (list.size() > 1) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); for (CanonicalType c : list) { @@ -3109,7 +3109,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) expression.append(" and (" + discriminator + ".exists().not())"); else - throw new FHIRException( I18nConstants.DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0, discriminator, ed.getId()); + throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); } else if (criteriaElement.hasFixed()) { buildFixedExpression(ed, expression, discriminator, criteriaElement); } else if (criteriaElement.hasPattern()) { @@ -3127,15 +3127,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!anyFound) { if (slicer.getSlicing().getDiscriminator().size() > 1) - throw new DefinitionException( I18nConstants.COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators); + throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); else - throw new DefinitionException( I18nConstants.COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators); + throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); } try { n = fpe.parse(fixExpr(expression.toString())); } catch (FHIRLexerException e) { - throw new FHIRException( I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, expression, profile.getUrl(), path, e.getMessage()); + throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); } fpeTime = fpeTime + (System.nanoTime() - t); ed.setUserData("slice.expression.cache", n); @@ -3163,7 +3163,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat msg = fpe.forLog(); } catch (Exception ex) { ex.printStackTrace(); - throw new FHIRException( I18nConstants.PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___, profile.getUrl(), path, n, ex.getMessage()); + throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); } return ok; } @@ -3183,13 +3183,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat expression.append(" and "); buildIdentifierExpression(ed, expression, discriminator, ii); } else - throw new DefinitionException( I18nConstants.UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), pattern.getClass().getName()); + throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); } private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) throws DefinitionException { if (ii.hasExtension()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); boolean first = true; expression.append(discriminator + ".where("); if (ii.hasSystem()) { @@ -3223,15 +3223,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) throws DefinitionException { if (cc.hasText()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); if (!cc.hasCoding()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); if (cc.hasExtension()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); boolean firstCoding = true; for (Coding c : cc.getCoding()) { if (c.hasExtension()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); if (firstCoding) firstCoding = false; else expression.append(" and "); expression.append(discriminator + ".coding.where("); @@ -3262,7 +3262,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodingExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Coding c) throws DefinitionException { if (c.hasExtension()) - throw new DefinitionException( I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId()); + throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); expression.append(discriminator + ".where("); boolean first = true; if (c.hasSystem()) { @@ -3318,7 +3318,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fixed instanceof BooleanType) { expression.append(((BooleanType) fixed).asStringValue()); } else - throw new DefinitionException( I18nConstants.UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), fixed.getClass().getName()); + throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); expression.append(" in " + discriminator + ")"); } } @@ -3680,7 +3680,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat byte[] json = bs.toByteArray(); switch (v) { case DSTU1: - throw new FHIRException( I18nConstants.UNSUPPORTED_VERSION_R1); + throw new FHIRException("Unsupported version R1"); case DSTU2: org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); Resource r5 = VersionConvertor_10_50.convertResource(r2); @@ -4622,7 +4622,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); if (dt == null) - throw new DefinitionException( I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType); + throw new DefinitionException("Unable to resolve actual type " + actualType); trackUsage(dt, hostContext, element); childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); @@ -4980,7 +4980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String errorContext = "profile " + profile.getUrl(); if (!resource.getChildValue("id").isEmpty()) errorContext += "; instance " + resource.getChildValue("id"); - throw new DefinitionException( I18nConstants.SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___, slicer.getPath(), slicer.getId(), errorContext); + throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); } slicer = ed; process = false; @@ -5104,7 +5104,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (tail.equals(t.getId())) return t; } - throw new DefinitionException( I18nConstants.UNABLE_TO_FIND_ELEMENT_WITH_ID_, tail); + throw new DefinitionException("Unable to find element with id '" + tail + "'"); } private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { @@ -5176,7 +5176,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { n = fpe.parse(fixExpr(inv.getExpression())); } catch (FHIRLexerException e) { - throw new FHIRException( I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage()); + throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); } fpeTime = fpeTime + (System.nanoTime() - t); inv.setUserData("validator.expression.cache", n); @@ -5224,11 +5224,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateObservation(List errors, Element element, NodeStack stack) { // all observations should have a subject, a performer, and a time - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); List performers = new ArrayList<>(); element.getNamedChildren("performer", performers); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, + "All observations should have an effectiveDateTime or an effectivePeriod"); } /* @@ -5315,9 +5316,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); return msgs.size() == 0; } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { - throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE); + throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); } else { - throw new FHIRException( I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET); + throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java index be67cee0d..0cbc9ded6 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -226,46 +226,4 @@ public class I18nConstants { public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; - public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET = "Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set"; - public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE = "Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done"; - public final static String ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD = "All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod"; - public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; - public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; - public final static String PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__ = "Problem_processing_expression__in_profile__path__"; - public final static String UNABLE_TO_FIND_ELEMENT_WITH_ID_ = "Unable_to_find_element_with_id_"; - public final static String SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___ = "Slice_encountered_midway_through_set_path___id___"; - public final static String UNABLE_TO_RESOLVE_ACTUAL_TYPE_ = "Unable_to_resolve_actual_type_"; - public final static String UNSUPPORTED_VERSION_R1 = "Unsupported_version_R1"; - public final static String UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_value_type_for_discriminator_for_slice__"; - public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; - public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_"; - public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_"; - public final static String UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; - public final static String UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_pattern_type_for_discriminator_for_slice__"; - public final static String PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___ = "Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___"; - public final static String COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions"; - public final static String COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions"; - public final static String DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0 = "Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0"; - public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_"; - public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_only_one_type__in_profile_"; - public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type__in_profile_"; - public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES = "Discriminator__is_based_on_type_but_slice__in__has_no_types"; - public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_ = "Discriminator__is_based_on_type_but_slice__in__has_multiple_types_"; - public final static String FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_ = "Found__items_for__resolving_discriminator__from_"; - public final static String UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find__resolving_discriminator__from_"; - public final static String UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find_resource__at__resolving_discriminator__from_"; - public final static String NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_ = "No_reference_resolving_discriminator__from_"; - public final static String UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_ = "Unable_to_resolve_element__in_profile_"; - public final static String UNABLE_TO_RESOLVE_PROFILE_ = "Unable_to_resolve_profile_"; - public final static String RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED = "Resource_resolution_services_not_provided"; - public final static String UNRECOGNISED_EXTENSION_CONTEXT_ = "Unrecognised_extension_context_"; - public final static String UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT = "Unable_to_locate_the_profile__in_order_to_validate_against_it"; - public final static String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet"; - public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT = "Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element"; - public final static String NOT_SUPPORTED_YET = "Not_supported_yet"; - public final static String UNABLE_TO_RESOLVE_ = "Unable_to_resolve_"; - public final static String NOT_DONE_YET__RESOLVE__LOCALLY_2 = "Not_done_yet__resolve__locally_2"; - public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION = "Not_done_yet_ValidatorHostServicesexecuteFunction"; - public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION = "Not_done_yet_ValidatorHostServicescheckFunction"; - public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_ = "Not_done_yet_ValidatorHostServicesresolveFunction_"; } diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index e72034bb7..a8999d262 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -135,6 +135,7 @@ Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty @@ -223,45 +224,3 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set -Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done -All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod -All_observations_should_have_a_performer = All observations should have a performer -All_observations_should_have_a_subject = All observations should have a subject -Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} -Unable_to_find_element_with_id_ = Unable to find element with id '{0}' -Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} -Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} -Unsupported_version_R1 = Unsupported version R1 -Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} -Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} -Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} -Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} -Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} -Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} -Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} -Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions -Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions -Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 -Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) -Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) -Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) -Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types -Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} -Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} -Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} -Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} -No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} -Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} -Unable_to_resolve_profile_ = Unable to resolve profile {0} -Resource_resolution_services_not_provided = Resource resolution services not provided -Unrecognised_extension_context_ = Unrecognised extension context {0} -Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it -Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet -Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element -Not_supported_yet = Not supported yet -Unable_to_resolve_ = Unable to resolve {0} -Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) -Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) -Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) -Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index e72034bb7..a8999d262 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -135,6 +135,7 @@ Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty @@ -223,45 +224,3 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set -Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done -All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod -All_observations_should_have_a_performer = All observations should have a performer -All_observations_should_have_a_subject = All observations should have a subject -Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} -Unable_to_find_element_with_id_ = Unable to find element with id '{0}' -Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} -Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} -Unsupported_version_R1 = Unsupported version R1 -Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} -Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} -Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} -Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} -Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} -Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} -Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} -Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions -Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions -Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 -Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) -Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) -Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) -Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types -Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} -Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} -Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} -Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} -No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} -Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} -Unable_to_resolve_profile_ = Unable to resolve profile {0} -Resource_resolution_services_not_provided = Resource resolution services not provided -Unrecognised_extension_context_ = Unrecognised extension context {0} -Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it -Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet -Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element -Not_supported_yet = Not supported yet -Unable_to_resolve_ = Unable to resolve {0} -Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) -Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) -Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) -Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} From 02ff8c5ddf6bf61e97cffca6477b81e6b23c24e2 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 20:54:09 +0100 Subject: [PATCH 18/52] extracted: bpCheck|warningOrError|suppressedwarning --- .../main/java/org/hl7/fhir/validation/BaseValidator.java | 2 +- .../hl7/fhir/validation/instance/InstanceValidator.java | 7 +++---- .../java/org/hl7/fhir/validation/utils/I18nConstants.java | 4 ++++ .../src/main/resources/Messages.properties | 3 +++ .../src/main/resources/Messages_de_DE.properties | 3 +++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index cac38fb49..cc8576f70 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -146,7 +146,7 @@ public class BaseValidator { } - private String formatMessage(String theMessage, Object... theMessageArguments) { + protected String formatMessage(String theMessage, Object... theMessageArguments) { String message; if (theMessageArguments != null && theMessageArguments.length > 0) { message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 05290ed97..76cb6f88e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -5224,12 +5224,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateObservation(List errors, Element element, NodeStack stack) { // all observations should have a subject, a performer, and a time - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, "All observations should have a subject"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("subject") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT); List performers = new ArrayList<>(); element.getNamedChildren("performer", performers); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, "All observations should have a performer"); - bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, - "All observations should have an effectiveDateTime or an effectivePeriod"); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), performers.size() > 0, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER); + bpCheck(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), element.getNamedChild("effectiveDateTime") != null || element.getNamedChild("effectivePeriod") != null, I18nConstants.ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD); } /* diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java index 0cbc9ded6..add24e6e8 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -226,4 +226,8 @@ public class I18nConstants { public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD = "All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; + public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; + } diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index a8999d262..a5f5f2e3d 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -224,3 +224,6 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod +All_observations_should_have_a_performer = All observations should have a performer +All_observations_should_have_a_subject = All observations should have a subject diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index a8999d262..a5f5f2e3d 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -224,3 +224,6 @@ Terminology_TX_System_Unknown = Unknown Code System '{0}' Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod +All_observations_should_have_a_performer = All observations should have a performer +All_observations_should_have_a_subject = All observations should have a subject From 446c9462a04d778625498fb290fe7bfcfbad2573 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 21:57:57 +0100 Subject: [PATCH 19/52] String Constant and properties ordered A->Z --- .../fhir/validation/utils/I18nConstants.java | 408 ++++++++--------- .../src/main/resources/Messages.properties | 418 +++++++++--------- .../main/resources/Messages_de_DE.properties | 418 +++++++++--------- 3 files changed, 622 insertions(+), 622 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java index add24e6e8..934f60099 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -2,230 +2,230 @@ package org.hl7.fhir.validation.utils; public class I18nConstants { - public final static String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited"; - public final static String RESOURCE_RES_ID_MISSING = "Resource_RES_ID_Missing"; - public final static String VALIDATION_VAL_PROFILE_WRONGTYPE = "Validation_VAL_Profile_WrongType"; - public final static String VALIDATION_VAL_PROFILE_NODEFINITION = "Validation_VAL_Profile_NoDefinition"; - public final static String VALIDATION_BUNDLE_MESSAGE = "Validation_BUNDLE_Message"; - public final static String VALIDATION_VAL_PROFILE_MATCHMULTIPLE = "Validation_VAL_Profile_MatchMultiple"; - public final static String VALIDATION_VAL_PROFILE_SLICEORDER = "Validation_VAL_Profile_SliceOrder"; - public final static String VALIDATION_VAL_PROFILE_OUTOFORDER = "Validation_VAL_Profile_OutOfOrder"; - public final static String VALIDATION_VAL_PROFILE_NOTALLOWED = "Validation_VAL_Profile_NotAllowed"; - public final static String VALIDATION_VAL_PROFILE_NOTSLICE = "Validation_VAL_Profile_NotSlice"; - public final static String VALIDATION_VAL_PROFILE_MAXIMUM = "Validation_VAL_Profile_Maximum"; - public final static String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax"; - public final static String VALIDATION_VAL_PROFILE_MINIMUM = "Validation_VAL_Profile_Minimum"; - public final static String VALIDATION_VAL_PROFILE_NOCHECKMIN = "Validation_VAL_Profile_NoCheckMin"; - public final static String VALIDATION_VAL_PROFILE_MULTIPLEMATCHES = "Validation_VAL_Profile_MultipleMatches"; - public final static String VALIDATION_VAL_PROFILE_NOMATCH = "Validation_VAL_Profile_NoMatch"; - public final static String VALIDATION_VAL_UNKNOWN_PROFILE = "Validation_VAL_Unknown_Profile"; - public final static String VALIDATION_VAL_NOTYPE = "Validation_VAL_NoType"; - public final static String VALIDATION_VAL_CONTENT_UNKNOWN = "Validation_VAL_Content_Unknown"; - public final static String EXTENSION_EXT_URL_ABSOLUTE = "Extension_EXT_URL_Absolute"; - public final static String EXTENSION_EXT_URL_NOTFOUND = "Extension_EXT_Url_NotFound"; - public final static String VALIDATION_VAL_PROFILE_NOTYPE = "Validation_VAL_Profile_NoType"; + public final static String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical"; public final static String BUNDLE_BUNDLE_ENTRY_DOCUMENT = "Bundle_BUNDLE_Entry_Document"; - public final static String BUNDLE_BUNDLE_ENTRY_TYPE3 = "Bundle_BUNDLE_Entry_Type3"; - public final static String BUNDLE_BUNDLE_ENTRY_TYPE2 = "Bundle_BUNDLE_Entry_Type2"; + public final static String BUNDLE_BUNDLE_ENTRY_IDURLMISMATCH = "Bundle_BUNDLE_Entry_IdUrlMismatch"; + public final static String BUNDLE_BUNDLE_ENTRY_MISMATCHIDURL = "Bundle_BUNDLE_Entry_MismatchIdUrl"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFIRST = "Bundle_BUNDLE_Entry_NoFirst"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE = "Bundle_BUNDLE_Entry_NoFirstResource"; + public final static String BUNDLE_BUNDLE_ENTRY_NOFULLURL = "Bundle_BUNDLE_Entry_NoFullUrl"; public final static String BUNDLE_BUNDLE_ENTRY_NOPROFILE = "Bundle_BUNDLE_Entry_NoProfile"; - public final static String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type"; public final static String BUNDLE_BUNDLE_ENTRY_NOTFOUND = "Bundle_BUNDLE_Entry_NotFound"; public final static String BUNDLE_BUNDLE_ENTRY_ORPHAN = "Bundle_BUNDLE_Entry_Orphan"; - public final static String BUNDLE_BUNDLE_ENTRY_IDURLMISMATCH = "Bundle_BUNDLE_Entry_IdUrlMismatch"; - public final static String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical"; - public final static String BUNDLE_BUNDLE_ENTRY_MISMATCHIDURL = "Bundle_BUNDLE_Entry_MismatchIdUrl"; - public final static String BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE = "Bundle_BUNDLE_Entry_NoFirstResource"; - public final static String BUNDLE_BUNDLE_ENTRY_NOFIRST = "Bundle_BUNDLE_Entry_NoFirst"; - public final static String QUESTIONNAIRE_QR_ITEM_CODINGNOOPTIONS = "Questionnaire_QR_Item_CodingNoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_NOCODING = "Questionnaire_QR_Item_NoCoding"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSCODING = "Questionnaire_QR_Item_NoOptionsCoding"; - public final static String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_NOSTRING = "Questionnaire_QR_Item_NoString"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING = "Questionnaire_QR_Item_NoOptionsString"; - public final static String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_NOTIME = "Questionnaire_QR_Item_NoTime"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSTIME = "Questionnaire_QR_Item_NoOptionsTime"; - public final static String QUESTIONNAIRE_QR_ITEM_DATENOOPTIONS = "Questionnaire_QR_Item_DateNoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_NODATE = "Questionnaire_QR_Item_NoDate"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSDATE = "Questionnaire_QR_Item_NoOptionsDate"; - public final static String QUESTIONNAIRE_QR_ITEM_INTNOOPTIONS = "Questionnaire_QR_Item_IntNoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_NOINTEGER = "Questionnaire_QR_Item_NoInteger"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSINTEGER = "Questionnaire_QR_Item_NoOptionsInteger"; - public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONS = "Questionnaire_QR_Item_NoOptions"; - public final static String QUESTIONNAIRE_QR_ITEM_CODING = "Questionnaire_QR_Item_Coding"; - public final static String QUESTIONNAIRE_QR_ITEM_BADOPTION = "Questionnaire_QR_Item_BadOption"; - public final static String TERMINOLOGY_TX_VALUESET_NOTFOUND = "Terminology_TX_ValueSet_NotFound"; - public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE2 = "Questionnaire_QR_Item_WrongType2"; - public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType"; - public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED2 = "Questionnaire_QR_Item_NotEnabled2"; - public final static String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order"; - public final static String QUESTIONNAIRE_QR_ITEM_NOTFOUND = "Questionnaire_QR_Item_NotFound"; - public final static String QUESTIONNAIRE_QR_ITEM_NOLINKID = "Questionnaire_QR_Item_NoLinkId"; - public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI"; - public final static String QUESTIONNAIRE_QR_ITEM_DISPLAY = "Questionnaire_QR_Item_Display"; - public final static String QUESTIONNAIRE_QR_ITEM_NOTYPE = "Questionnaire_QR_Item_NoType"; - public final static String QUESTIONNAIRE_QR_ITEM_GROUP = "Questionnaire_QR_Item_Group"; - public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEA = "Questionnaire_QR_Item_OnlyOneA"; - public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED = "Questionnaire_QR_Item_NotEnabled"; - public final static String QUESTIONNAIRE_QR_ITEM_MISSING = "Questionnaire_QR_Item_Missing"; - public final static String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text"; - public final static String QUESTIONNAIRE_QR_Q_NOTFOUND = "Questionnaire_QR_Q_NotFound"; - public final static String QUESTIONNAIRE_QR_Q_NONE = "Questionnaire_QR_Q_None"; - public final static String CODESYSTEM_CS_VS_INCLUDEDETAILS = "CodeSystem_CS_VS_IncludeDetails"; - public final static String CODESYSTEM_CS_VS_WRONGSYSTEM = "CodeSystem_CS_VS_WrongSystem"; - public final static String CODESYSTEM_CS_VS_INVALID = "CodeSystem_CS_VS_Invalid"; - public final static String CODESYSTEM_CS_VS_MISMATCH = "CodeSystem_CS_VS_MisMatch"; - public final static String CAPABALITYSTATEMENT_CS_SP_WRONGTYPE = "CapabalityStatement_CS_SP_WrongType"; - public final static String META_RES_SECURITY_DUPLICATE = "Meta_RES_Security_Duplicate"; - public final static String LANGUAGE_XHTML_LANG_DIFFERENT2 = "Language_XHTML_Lang_Different2"; - public final static String LANGUAGE_XHTML_LANG_MISSING3 = "Language_XHTML_Lang_Missing3"; - public final static String LANGUAGE_XHTML_LANG_DIFFERENT1 = "Language_XHTML_Lang_Different1"; - public final static String LANGUAGE_XHTML_LANG_MISSING2 = "Language_XHTML_Lang_Missing2"; - public final static String LANGUAGE_XHTML_LANG_MISSING1 = "Language_XHTML_Lang_Missing1"; - public final static String QUESTIONNAIRE_Q_ENABLEWHEN_AFTER = "Questionnaire_Q_EnableWhen_After"; - public final static String QUESTIONNAIRE_Q_ENABLEWHEN_SELF = "Questionnaire_Q_EnableWhen_Self"; - public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOTARGET = "Questionnaire_Q_EnableWhen_NoTarget"; - public final static String QUESTIONNAIRE_Q_ENABLEWHEN_ISINNER = "Questionnaire_Q_EnableWhen_IsInner"; - public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOLINK = "Questionnaire_Q_EnableWhen_NoLink"; - public final static String VALIDATION_VAL_PROFILE_NOSNAPSHOT = "Validation_VAL_Profile_NoSnapshot"; - public final static String VALIDATION_VAL_PROFILE_UNKNOWN = "Validation_VAL_Profile_Unknown"; - public final static String BUNDLE_BUNDLE_ENTRY_NOFULLURL = "Bundle_BUNDLE_Entry_NoFullUrl"; - public final static String BUNDLE_BUNDLE_NOT_LOCAL = "Bundle_BUNDLE_Not_Local"; - public final static String REFERENCE_REF_RESOURCETYPE = "Reference_REF_ResourceType"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE2 = "Bundle_BUNDLE_Entry_Type2"; + public final static String BUNDLE_BUNDLE_ENTRY_TYPE3 = "Bundle_BUNDLE_Entry_Type3"; + public final static String BUNDLE_BUNDLE_FULLURL_MISSING = "Bundle_BUNDLE_FullUrl_Missing"; public final static String BUNDLE_BUNDLE_FULLURL_NEEDVERSION = "Bundle_BUNDLE_FullUrl_NeedVersion"; public final static String BUNDLE_BUNDLE_MULTIPLEMATCHES = "Bundle_BUNDLE_MultipleMatches"; - public final static String REFERENCE_REF_FORMAT2 = "Reference_REF_Format2"; - public final static String REFERENCE_REF_FORMAT1 = "Reference_REF_Format1"; - public final static String BUNDLE_BUNDLE_FULLURL_MISSING = "Bundle_BUNDLE_FullUrl_Missing"; + public final static String BUNDLE_BUNDLE_NOT_LOCAL = "Bundle_BUNDLE_Not_Local"; public final static String BUNDLE_MSG_EVENT_COUNT = "Bundle_MSG_Event_Count"; - public final static String REFERENCE_REF_BADTARGETTYPE2 = "Reference_REF_BadTargetType2"; - public final static String REFERENCE_REF_NOTFOUND_BUNDLE = "Reference_REF_NotFound_Bundle"; + public final static String CAPABALITYSTATEMENT_CS_SP_WRONGTYPE = "CapabalityStatement_CS_SP_WrongType"; + public final static String CODESYSTEM_CS_VS_INCLUDEDETAILS = "CodeSystem_CS_VS_IncludeDetails"; + public final static String CODESYSTEM_CS_VS_INVALID = "CodeSystem_CS_VS_Invalid"; + public final static String CODESYSTEM_CS_VS_MISMATCH = "CodeSystem_CS_VS_MisMatch"; + public final static String CODESYSTEM_CS_VS_WRONGSYSTEM = "CodeSystem_CS_VS_WrongSystem"; + public final static String EXTENSION_EXT_CONTEXT_WRONG = "Extension_EXT_Context_Wrong"; + public final static String EXTENSION_EXT_COUNT_MISMATCH = "Extension_EXT_Count_Mismatch"; + public final static String EXTENSION_EXT_COUNT_NOTFOUND = "Extension_EXT_Count_NotFound"; + public final static String EXTENSION_EXT_FIXED_BANNED = "Extension_EXT_Fixed_Banned"; + public final static String EXTENSION_EXT_MODIFIER_MISMATCHN = "Extension_EXT_Modifier_MismatchN"; + public final static String EXTENSION_EXT_MODIFIER_MISMATCHY = "Extension_EXT_Modifier_MismatchY"; + public final static String EXTENSION_EXT_MODIFIER_N = "Extension_EXT_Modifier_N"; + public final static String EXTENSION_EXT_MODIFIER_Y = "Extension_EXT_Modifier_Y"; + public final static String EXTENSION_EXT_SIMPLE = "Extension_EXT_Simple"; + public final static String EXTENSION_EXT_SUBEXTENSION_INVALID = "Extension_EXT_SubExtension_Invalid"; + public final static String EXTENSION_EXT_TYPE = "Extension_EXT_Type"; + public final static String EXTENSION_EXT_UNKNOWN = "Extension_EXT_Unknown"; + public final static String EXTENSION_EXT_UNKNOWN_NOTHERE = "Extension_EXT_Unknown_NotHere"; + public final static String EXTENSION_EXT_URL_ABSOLUTE = "Extension_EXT_URL_Absolute"; + public final static String EXTENSION_EXT_URL_NOTFOUND = "Extension_EXT_Url_NotFound"; + public final static String EXTENSION_EXT_VERSION_INTERNAL = "Extension_EXT_Version_Internal"; + public final static String EXTENSION_EXT_VERSION_INVALID = "Extension_EXT_Version_Invalid"; + public final static String EXTENSION_EXT_VERSION_INVALIDID = "Extension_EXT_Version_InvalidId"; + public final static String EXTENSION_EXT_VERSION_NOCHANGE = "Extension_EXT_Version_NoChange"; + public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_FAMILY = "Fixed_Type_Checks_DT_Name_Family"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_GIVEN = "Fixed_Type_Checks_DT_Name_Given"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_PREFIX = "Fixed_Type_Checks_DT_Name_Prefix"; + public final static String FIXED_TYPE_CHECKS_DT_NAME_SUFFIX = "Fixed_Type_Checks_DT_Name_Suffix"; + public final static String INTERNAL_INT_BAD_TYPE = "Internal_INT_Bad_Type"; + public final static String LANGUAGE_XHTML_LANG_DIFFERENT1 = "Language_XHTML_Lang_Different1"; + public final static String LANGUAGE_XHTML_LANG_DIFFERENT2 = "Language_XHTML_Lang_Different2"; + public final static String LANGUAGE_XHTML_LANG_MISSING1 = "Language_XHTML_Lang_Missing1"; + public final static String LANGUAGE_XHTML_LANG_MISSING2 = "Language_XHTML_Lang_Missing2"; + public final static String LANGUAGE_XHTML_LANG_MISSING3 = "Language_XHTML_Lang_Missing3"; + public final static String META_RES_SECURITY_DUPLICATE = "Meta_RES_Security_Duplicate"; + public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; + public final static String PROFILE_EXT_NOT_HERE = "Profile_EXT_Not_Here"; + public final static String PROFILE_VAL_MISSINGELEMENT = "Profile_VAL_MissingElement"; + public final static String PROFILE_VAL_NOTALLOWED = "Profile_VAL_NotAllowed"; + public final static String QUESTIONNAIRE_QR_ITEM_BADOPTION = "Questionnaire_QR_Item_BadOption"; + public final static String QUESTIONNAIRE_QR_ITEM_CODING = "Questionnaire_QR_Item_Coding"; + public final static String QUESTIONNAIRE_QR_ITEM_CODINGNOOPTIONS = "Questionnaire_QR_Item_CodingNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_DATENOOPTIONS = "Questionnaire_QR_Item_DateNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_DISPLAY = "Questionnaire_QR_Item_Display"; + public final static String QUESTIONNAIRE_QR_ITEM_GROUP = "Questionnaire_QR_Item_Group"; + public final static String QUESTIONNAIRE_QR_ITEM_INTNOOPTIONS = "Questionnaire_QR_Item_IntNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_MISSING = "Questionnaire_QR_Item_Missing"; + public final static String QUESTIONNAIRE_QR_ITEM_NOCODING = "Questionnaire_QR_Item_NoCoding"; + public final static String QUESTIONNAIRE_QR_ITEM_NODATE = "Questionnaire_QR_Item_NoDate"; + public final static String QUESTIONNAIRE_QR_ITEM_NOINTEGER = "Questionnaire_QR_Item_NoInteger"; + public final static String QUESTIONNAIRE_QR_ITEM_NOLINKID = "Questionnaire_QR_Item_NoLinkId"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONS = "Questionnaire_QR_Item_NoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSCODING = "Questionnaire_QR_Item_NoOptionsCoding"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSDATE = "Questionnaire_QR_Item_NoOptionsDate"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSINTEGER = "Questionnaire_QR_Item_NoOptionsInteger"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSSTRING = "Questionnaire_QR_Item_NoOptionsString"; + public final static String QUESTIONNAIRE_QR_ITEM_NOOPTIONSTIME = "Questionnaire_QR_Item_NoOptionsTime"; + public final static String QUESTIONNAIRE_QR_ITEM_NOSTRING = "Questionnaire_QR_Item_NoString"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED = "Questionnaire_QR_Item_NotEnabled"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTENABLED2 = "Questionnaire_QR_Item_NotEnabled2"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTFOUND = "Questionnaire_QR_Item_NotFound"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTIME = "Questionnaire_QR_Item_NoTime"; + public final static String QUESTIONNAIRE_QR_ITEM_NOTYPE = "Questionnaire_QR_Item_NoType"; + public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEA = "Questionnaire_QR_Item_OnlyOneA"; + public final static String QUESTIONNAIRE_QR_ITEM_ONLYONEI = "Questionnaire_QR_Item_OnlyOneI"; + public final static String QUESTIONNAIRE_QR_ITEM_ORDER = "Questionnaire_QR_Item_Order"; + public final static String QUESTIONNAIRE_QR_ITEM_STRINGNOOPTIONS = "Questionnaire_QR_Item_StringNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_TEXT = "Questionnaire_QR_Item_Text"; + public final static String QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS = "Questionnaire_QR_Item_TimeNoOptions"; + public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE = "Questionnaire_QR_Item_WrongType"; + public final static String QUESTIONNAIRE_QR_ITEM_WRONGTYPE2 = "Questionnaire_QR_Item_WrongType2"; + public final static String QUESTIONNAIRE_QR_Q_NONE = "Questionnaire_QR_Q_None"; + public final static String QUESTIONNAIRE_QR_Q_NOTFOUND = "Questionnaire_QR_Q_NotFound"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_AFTER = "Questionnaire_Q_EnableWhen_After"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_ISINNER = "Questionnaire_Q_EnableWhen_IsInner"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOLINK = "Questionnaire_Q_EnableWhen_NoLink"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_NOTARGET = "Questionnaire_Q_EnableWhen_NoTarget"; + public final static String QUESTIONNAIRE_Q_ENABLEWHEN_SELF = "Questionnaire_Q_EnableWhen_Self"; public final static String REFERENCE_REF_AGGREGATION = "Reference_REF_Aggregation"; public final static String REFERENCE_REF_BADTARGETTYPE = "Reference_REF_BadTargetType"; - public final static String REFERENCE_REF_MULTIPLEMATCHES = "Reference_REF_MultipleMatches"; + public final static String REFERENCE_REF_BADTARGETTYPE2 = "Reference_REF_BadTargetType2"; public final static String REFERENCE_REF_CANTMATCHCHOICE = "Reference_REF_CantMatchChoice"; public final static String REFERENCE_REF_CANTMATCHTYPE = "Reference_REF_CantMatchType"; - public final static String REFERENCE_REF_CANTRESOLVEPROFILE = "Reference_REF_CantResolveProfile"; - public final static String REFERENCE_REF_NOTYPE = "Reference_REF_NoType"; - public final static String REFERENCE_REF_WRONGTARGET = "Reference_REF_WrongTarget"; public final static String REFERENCE_REF_CANTRESOLVE = "Reference_REF_CantResolve"; + public final static String REFERENCE_REF_CANTRESOLVEPROFILE = "Reference_REF_CantResolveProfile"; + public final static String REFERENCE_REF_FORMAT1 = "Reference_REF_Format1"; + public final static String REFERENCE_REF_FORMAT2 = "Reference_REF_Format2"; + public final static String REFERENCE_REF_MULTIPLEMATCHES = "Reference_REF_MultipleMatches"; public final static String REFERENCE_REF_NODISPLAY = "Reference_REF_NoDisplay"; + public final static String REFERENCE_REF_NOTFOUND_BUNDLE = "Reference_REF_NotFound_Bundle"; + public final static String REFERENCE_REF_NOTYPE = "Reference_REF_NoType"; + public final static String REFERENCE_REF_RESOURCETYPE = "Reference_REF_ResourceType"; + public final static String REFERENCE_REF_WRONGTARGET = "Reference_REF_WrongTarget"; + public final static String RESOURCE_RES_ID_MISSING = "Resource_RES_ID_Missing"; + public final static String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited"; + public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; + public final static String TERMINOLOGY_TX_BINDING_CANTCHECK = "Terminology_TX_Binding_CantCheck"; + public final static String TERMINOLOGY_TX_BINDING_MISSING = "Terminology_TX_Binding_Missing"; + public final static String TERMINOLOGY_TX_BINDING_MISSING2 = "Terminology_TX_Binding_Missing2"; + public final static String TERMINOLOGY_TX_BINDING_NOSERVER = "Terminology_TX_Binding_NoServer"; + public final static String TERMINOLOGY_TX_BINDING_NOSOURCE = "Terminology_TX_Binding_NoSource"; public final static String TERMINOLOGY_TX_BINDING_NOSOURCE2 = "Terminology_TX_Binding_NoSource2"; - public final static String TERMINOLOGY_TX_NOVALID_18 = "Terminology_TX_NoValid_18"; - public final static String TERMINOLOGY_TX_NOVALID_17 = "Terminology_TX_NoValid_17"; - public final static String TERMINOLOGY_TX_NOVALID_16 = "Terminology_TX_NoValid_16"; + public final static String TERMINOLOGY_TX_CODE_NOTVALID = "Terminology_TX_Code_NotValid"; + public final static String TERMINOLOGY_TX_CODE_UNKNOWN = "Terminology_TX_Code_Unknown"; + public final static String TERMINOLOGY_TX_CODE_VALUESET = "Terminology_TX_Code_ValueSet"; + public final static String TERMINOLOGY_TX_CODE_VALUESETMAX = "Terminology_TX_Code_ValueSetMax"; + public final static String TERMINOLOGY_TX_CODE_VALUESET_EXT = "Terminology_TX_Code_ValueSet_Ext"; + public final static String TERMINOLOGY_TX_CODING_COUNT = "Terminology_TX_Coding_Count"; + public final static String TERMINOLOGY_TX_CONFIRM_1 = "Terminology_TX_Confirm_1"; + public final static String TERMINOLOGY_TX_CONFIRM_2 = "Terminology_TX_Confirm_2"; + public final static String TERMINOLOGY_TX_CONFIRM_3 = "Terminology_TX_Confirm_3"; + public final static String TERMINOLOGY_TX_CONFIRM_4 = "Terminology_TX_Confirm_4"; + public final static String TERMINOLOGY_TX_CONFIRM_5 = "Terminology_TX_Confirm_5"; + public final static String TERMINOLOGY_TX_CONFIRM_6 = "Terminology_TX_Confirm_6"; + public final static String TERMINOLOGY_TX_DISPLAY_WRONG = "Terminology_TX_Display_Wrong"; + public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT = "Terminology_TX_Error_CodeableConcept"; + public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX = "Terminology_TX_Error_CodeableConcept_Max"; + public final static String TERMINOLOGY_TX_ERROR_CODING1 = "Terminology_TX_Error_Coding1"; + public final static String TERMINOLOGY_TX_ERROR_CODING2 = "Terminology_TX_Error_Coding2"; + public final static String TERMINOLOGY_TX_NOVALID_1 = "Terminology_TX_NoValid_1"; + public final static String TERMINOLOGY_TX_NOVALID_10 = "Terminology_TX_NoValid_10"; + public final static String TERMINOLOGY_TX_NOVALID_11 = "Terminology_TX_NoValid_11"; + public final static String TERMINOLOGY_TX_NOVALID_12 = "Terminology_TX_NoValid_12"; + public final static String TERMINOLOGY_TX_NOVALID_13 = "Terminology_TX_NoValid_13"; + public final static String TERMINOLOGY_TX_NOVALID_14 = "Terminology_TX_NoValid_14"; public final static String TERMINOLOGY_TX_NOVALID_15 = "Terminology_TX_NoValid_15"; + public final static String TERMINOLOGY_TX_NOVALID_16 = "Terminology_TX_NoValid_16"; + public final static String TERMINOLOGY_TX_NOVALID_17 = "Terminology_TX_NoValid_17"; + public final static String TERMINOLOGY_TX_NOVALID_18 = "Terminology_TX_NoValid_18"; + public final static String TERMINOLOGY_TX_NOVALID_2 = "Terminology_TX_NoValid_2"; + public final static String TERMINOLOGY_TX_NOVALID_3 = "Terminology_TX_NoValid_3"; + public final static String TERMINOLOGY_TX_NOVALID_4 = "Terminology_TX_NoValid_4"; + public final static String TERMINOLOGY_TX_NOVALID_5 = "Terminology_TX_NoValid_5"; + public final static String TERMINOLOGY_TX_NOVALID_6 = "Terminology_TX_NoValid_6"; + public final static String TERMINOLOGY_TX_NOVALID_7 = "Terminology_TX_NoValid_7"; + public final static String TERMINOLOGY_TX_NOVALID_8 = "Terminology_TX_NoValid_8"; + public final static String TERMINOLOGY_TX_NOVALID_9 = "Terminology_TX_NoValid_9"; + public final static String TERMINOLOGY_TX_SYSTEM_INVALID = "Terminology_TX_System_Invalid"; + public final static String TERMINOLOGY_TX_SYSTEM_NOTKNOWN = "Terminology_TX_System_NotKnown"; + public final static String TERMINOLOGY_TX_SYSTEM_RELATIVE = "Terminology_TX_System_Relative"; + public final static String TERMINOLOGY_TX_SYSTEM_UNKNOWN = "Terminology_TX_System_Unknown"; + public final static String TERMINOLOGY_TX_SYSTEM_VALUESET = "Terminology_TX_System_ValueSet"; + public final static String TERMINOLOGY_TX_SYSTEM_VALUESET2 = "Terminology_TX_System_ValueSet2"; + public final static String TERMINOLOGY_TX_VALUESET_NOTFOUND = "Terminology_TX_ValueSet_NotFound"; public final static String TERMINOLOGY_TX_VALUESET_NOTFOUND2 = "Terminology_TX_ValueSet_NotFound2"; - public final static String XHTML_XHTML_NS_INVALID = "XHTML_XHTML_NS_InValid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_BASE64_VALID = "Type_Specific_Checks_DT_Base64_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_BOOLEAN_VALUE = "Type_Specific_Checks_DT_Boolean_Value"; + public final static String TYPE_SPECIFIC_CHECKS_DT_CODE_WS = "Type_Specific_Checks_DT_Code_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE = "Type_Specific_Checks_DT_DateTime_Reasonable"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX = "Type_Specific_Checks_DT_DateTime_Regex"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ = "Type_Specific_Checks_DT_DateTime_TZ"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID = "Type_Specific_Checks_DT_DateTime_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DATE_VALID = "Type_Specific_Checks_DT_Date_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE = "Type_Specific_Checks_DT_Decimal_Range"; + public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID = "Type_Specific_Checks_DT_Decimal_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM = "Type_Specific_Checks_DT_Identifier_System"; + public final static String TYPE_SPECIFIC_CHECKS_DT_ID_VALID = "Type_Specific_Checks_DT_ID_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID = "Type_Specific_Checks_DT_Instant_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER64_VALID = "Type_Specific_Checks_DT_Integer64_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_GT = "Type_Specific_Checks_DT_Integer_GT"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT = "Type_Specific_Checks_DT_Integer_LT"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0 = "Type_Specific_Checks_DT_Integer_LT0"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1 = "Type_Specific_Checks_DT_Integer_LT1"; + public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_VALID = "Type_Specific_Checks_DT_Integer_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_OID_START = "Type_Specific_Checks_DT_OID_Start"; + public final static String TYPE_SPECIFIC_CHECKS_DT_OID_VALID = "Type_Specific_Checks_DT_OID_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH = "Type_Specific_Checks_DT_Primitive_Length"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY = "Type_Specific_Checks_DT_Primitive_NotEmpty"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX = "Type_Specific_Checks_DT_Primitive_Regex"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT = "Type_Specific_Checks_DT_Primitive_ValueExt"; + public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS = "Type_Specific_Checks_DT_Primitive_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH = "Type_Specific_Checks_DT_String_Length"; + public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_WS = "Type_Specific_Checks_DT_String_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_TIME_VALID = "Type_Specific_Checks_DT_Time_Valid"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_OID = "Type_Specific_Checks_DT_URI_OID"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URI_WS = "Type_Specific_Checks_DT_URI_WS"; + public final static String TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE = "Type_Specific_Checks_DT_URL_Resolve"; + public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT = "Type_Specific_Checks_DT_UUID_Strat"; + public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_VAID = "Type_Specific_Checks_DT_UUID_Vaid"; + public final static String VALIDATION_BUNDLE_MESSAGE = "Validation_BUNDLE_Message"; + public final static String VALIDATION_VAL_CONTENT_UNKNOWN = "Validation_VAL_Content_Unknown"; + public final static String VALIDATION_VAL_NOTYPE = "Validation_VAL_NoType"; + public final static String VALIDATION_VAL_PROFILE_MATCHMULTIPLE = "Validation_VAL_Profile_MatchMultiple"; + public final static String VALIDATION_VAL_PROFILE_MAXIMUM = "Validation_VAL_Profile_Maximum"; + public final static String VALIDATION_VAL_PROFILE_MINIMUM = "Validation_VAL_Profile_Minimum"; + public final static String VALIDATION_VAL_PROFILE_MULTIPLEMATCHES = "Validation_VAL_Profile_MultipleMatches"; + public final static String VALIDATION_VAL_PROFILE_NOCHECKMAX = "Validation_VAL_Profile_NoCheckMax"; + public final static String VALIDATION_VAL_PROFILE_NOCHECKMIN = "Validation_VAL_Profile_NoCheckMin"; + public final static String VALIDATION_VAL_PROFILE_NODEFINITION = "Validation_VAL_Profile_NoDefinition"; + public final static String VALIDATION_VAL_PROFILE_NOMATCH = "Validation_VAL_Profile_NoMatch"; + public final static String VALIDATION_VAL_PROFILE_NOSNAPSHOT = "Validation_VAL_Profile_NoSnapshot"; + public final static String VALIDATION_VAL_PROFILE_NOTALLOWED = "Validation_VAL_Profile_NotAllowed"; + public final static String VALIDATION_VAL_PROFILE_NOTSLICE = "Validation_VAL_Profile_NotSlice"; + public final static String VALIDATION_VAL_PROFILE_NOTYPE = "Validation_VAL_Profile_NoType"; + public final static String VALIDATION_VAL_PROFILE_OUTOFORDER = "Validation_VAL_Profile_OutOfOrder"; + public final static String VALIDATION_VAL_PROFILE_SLICEORDER = "Validation_VAL_Profile_SliceOrder"; + public final static String VALIDATION_VAL_PROFILE_UNKNOWN = "Validation_VAL_Profile_Unknown"; + public final static String VALIDATION_VAL_PROFILE_WRONGTYPE = "Validation_VAL_Profile_WrongType"; + public final static String VALIDATION_VAL_UNKNOWN_PROFILE = "Validation_VAL_Unknown_Profile"; public final static String XHTML_XHTML_ATTRIBUTE_ILLEGAL = "XHTML_XHTML_Attribute_Illegal"; public final static String XHTML_XHTML_ELEMENT_ILLEGAL = "XHTML_XHTML_Element_Illegal"; public final static String XHTML_XHTML_NAME_INVALID = "XHTML_XHTML_Name_Invalid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH = "Type_Specific_Checks_DT_Primitive_Length"; - public final static String TYPE_SPECIFIC_CHECKS_DT_CODE_WS = "Type_Specific_Checks_DT_Code_WS"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID = "Type_Specific_Checks_DT_Instant_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE = "Type_Specific_Checks_DT_DateTime_Reasonable"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX = "Type_Specific_Checks_DT_DateTime_Regex"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE = "Type_Specific_Checks_DT_Decimal_Range"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID = "Type_Specific_Checks_DT_Decimal_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT1 = "Type_Specific_Checks_DT_Integer_LT1"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT0 = "Type_Specific_Checks_DT_Integer_LT0"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_LT = "Type_Specific_Checks_DT_Integer_LT"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_GT = "Type_Specific_Checks_DT_Integer_GT"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER64_VALID = "Type_Specific_Checks_DT_Integer64_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_INTEGER_VALID = "Type_Specific_Checks_DT_Integer_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_BASE64_VALID = "Type_Specific_Checks_DT_Base64_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DATE_VALID = "Type_Specific_Checks_DT_Date_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_TIME_VALID = "Type_Specific_Checks_DT_Time_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID = "Type_Specific_Checks_DT_DateTime_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ = "Type_Specific_Checks_DT_DateTime_TZ"; - public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH = "Type_Specific_Checks_DT_String_Length"; - public final static String TYPE_SPECIFIC_CHECKS_DT_STRING_WS = "Type_Specific_Checks_DT_String_WS"; - public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY = "Type_Specific_Checks_DT_Primitive_NotEmpty"; - public final static String TYPE_SPECIFIC_CHECKS_DT_ID_VALID = "Type_Specific_Checks_DT_ID_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE = "Type_Specific_Checks_DT_URL_Resolve"; - public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_VAID = "Type_Specific_Checks_DT_UUID_Vaid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT = "Type_Specific_Checks_DT_UUID_Strat"; - public final static String TYPE_SPECIFIC_CHECKS_DT_OID_VALID = "Type_Specific_Checks_DT_OID_Valid"; - public final static String TYPE_SPECIFIC_CHECKS_DT_OID_START = "Type_Specific_Checks_DT_OID_Start"; - public final static String TYPE_SPECIFIC_CHECKS_DT_URI_WS = "Type_Specific_Checks_DT_URI_WS"; - public final static String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID"; - public final static String TYPE_SPECIFIC_CHECKS_DT_URI_OID = "Type_Specific_Checks_DT_URI_OID"; - public final static String TYPE_SPECIFIC_CHECKS_DT_BOOLEAN_VALUE = "Type_Specific_Checks_DT_Boolean_Value"; - public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX = "Type_Specific_Checks_DT_Primitive_Regex"; - public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS = "Type_Specific_Checks_DT_Primitive_WS"; - public final static String TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT = "Type_Specific_Checks_DT_Primitive_ValueExt"; - public final static String TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM = "Type_Specific_Checks_DT_Identifier_System"; - public final static String FIXED_TYPE_CHECKS_DT_NAME_SUFFIX = "Fixed_Type_Checks_DT_Name_Suffix"; - public final static String FIXED_TYPE_CHECKS_DT_NAME_PREFIX = "Fixed_Type_Checks_DT_Name_Prefix"; - public final static String FIXED_TYPE_CHECKS_DT_NAME_GIVEN = "Fixed_Type_Checks_DT_Name_Given"; - public final static String FIXED_TYPE_CHECKS_DT_NAME_FAMILY = "Fixed_Type_Checks_DT_Name_Family"; - public final static String EXTENSION_EXT_COUNT_NOTFOUND = "Extension_EXT_Count_NotFound"; - public final static String EXTENSION_EXT_COUNT_MISMATCH = "Extension_EXT_Count_Mismatch"; - public final static String EXTENSION_EXT_FIXED_BANNED = "Extension_EXT_Fixed_Banned"; - public final static String INTERNAL_INT_BAD_TYPE = "Internal_INT_Bad_Type"; + public final static String XHTML_XHTML_NS_INVALID = "XHTML_XHTML_NS_InValid"; public final static String _DT_FIXED_WRONG = "_DT_Fixed_Wrong"; - public final static String PROFILE_VAL_MISSINGELEMENT = "Profile_VAL_MissingElement"; - public final static String PROFILE_VAL_NOTALLOWED = "Profile_VAL_NotAllowed"; - public final static String PROFILE_EXT_NOT_HERE = "Profile_EXT_Not_Here"; - public final static String EXTENSION_EXT_CONTEXT_WRONG = "Extension_EXT_Context_Wrong"; - public final static String EXTENSION_EXT_TYPE = "Extension_EXT_Type"; - public final static String EXTENSION_EXT_SIMPLE = "Extension_EXT_Simple"; - public final static String EXTENSION_EXT_MODIFIER_N = "Extension_EXT_Modifier_N"; - public final static String EXTENSION_EXT_MODIFIER_Y = "Extension_EXT_Modifier_Y"; - public final static String EXTENSION_EXT_MODIFIER_MISMATCHN = "Extension_EXT_Modifier_MismatchN"; - public final static String EXTENSION_EXT_MODIFIER_MISMATCHY = "Extension_EXT_Modifier_MismatchY"; - public final static String EXTENSION_EXT_UNKNOWN = "Extension_EXT_Unknown"; - public final static String EXTENSION_EXT_UNKNOWN_NOTHERE = "Extension_EXT_Unknown_NotHere"; - public final static String EXTENSION_EXT_SUBEXTENSION_INVALID = "Extension_EXT_SubExtension_Invalid"; - public final static String EXTENSION_EXT_VERSION_INTERNAL = "Extension_EXT_Version_Internal"; - public final static String EXTENSION_EXT_VERSION_NOCHANGE = "Extension_EXT_Version_NoChange"; - public final static String EXTENSION_EXT_VERSION_INVALIDID = "Extension_EXT_Version_InvalidId"; - public final static String EXTENSION_EXT_VERSION_INVALID = "Extension_EXT_Version_Invalid"; - public final static String TERMINOLOGY_TX_ERROR_CODING2 = "Terminology_TX_Error_Coding2"; - public final static String TERMINOLOGY_TX_BINDING_NOSOURCE = "Terminology_TX_Binding_NoSource"; - public final static String TERMINOLOGY_TX_BINDING_CANTCHECK = "Terminology_TX_Binding_CantCheck"; - public final static String TERMINOLOGY_TX_ERROR_CODING1 = "Terminology_TX_Error_Coding1"; - public final static String TERMINOLOGY_TX_NOVALID_14 = "Terminology_TX_NoValid_14"; - public final static String TERMINOLOGY_TX_NOVALID_13 = "Terminology_TX_NoValid_13"; - public final static String TERMINOLOGY_TX_NOVALID_12 = "Terminology_TX_NoValid_12"; - public final static String TERMINOLOGY_TX_CONFIRM_6 = "Terminology_TX_Confirm_6"; - public final static String TERMINOLOGY_TX_CONFIRM_5 = "Terminology_TX_Confirm_5"; - public final static String TERMINOLOGY_TX_CONFIRM_4 = "Terminology_TX_Confirm_4"; - public final static String TERMINOLOGY_TX_BINDING_NOSERVER = "Terminology_TX_Binding_NoServer"; - public final static String TERMINOLOGY_TX_BINDING_MISSING2 = "Terminology_TX_Binding_Missing2"; - public final static String TERMINOLOGY_TX_SYSTEM_VALUESET2 = "Terminology_TX_System_ValueSet2"; - public final static String TERMINOLOGY_TX_SYSTEM_RELATIVE = "Terminology_TX_System_Relative"; - public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT_MAX = "Terminology_TX_Error_CodeableConcept_Max"; - public final static String TERMINOLOGY_TX_NOVALID_11 = "Terminology_TX_NoValid_11"; - public final static String TERMINOLOGY_TX_NOVALID_9 = "Terminology_TX_NoValid_9"; - public final static String TERMINOLOGY_TX_NOVALID_10 = "Terminology_TX_NoValid_10"; - public final static String TERMINOLOGY_TX_NOVALID_8 = "Terminology_TX_NoValid_8"; - public final static String TERMINOLOGY_TX_NOVALID_7 = "Terminology_TX_NoValid_7"; - public final static String TERMINOLOGY_TX_NOVALID_6 = "Terminology_TX_NoValid_6"; - public final static String TERMINOLOGY_TX_NOVALID_5 = "Terminology_TX_NoValid_5"; - public final static String TERMINOLOGY_TX_NOVALID_4 = "Terminology_TX_NoValid_4"; - public final static String TERMINOLOGY_TX_ERROR_CODEABLECONCEPT = "Terminology_TX_Error_CodeableConcept"; - public final static String TERMINOLOGY_TX_CODE_NOTVALID = "Terminology_TX_Code_NotValid"; - public final static String TERMINOLOGY_TX_NOVALID_3 = "Terminology_TX_NoValid_3"; - public final static String TERMINOLOGY_TX_NOVALID_2 = "Terminology_TX_NoValid_2"; - public final static String TERMINOLOGY_TX_NOVALID_1 = "Terminology_TX_NoValid_1"; - public final static String TERMINOLOGY_TX_CONFIRM_3 = "Terminology_TX_Confirm_3"; - public final static String TERMINOLOGY_TX_CONFIRM_2 = "Terminology_TX_Confirm_2"; - public final static String TERMINOLOGY_TX_CONFIRM_1 = "Terminology_TX_Confirm_1"; - public final static String TERMINOLOGY_TX_CODE_VALUESET_EXT = "Terminology_TX_Code_ValueSet_Ext"; - public final static String TERMINOLOGY_TX_CODE_VALUESETMAX = "Terminology_TX_Code_ValueSetMax"; - public final static String TERMINOLOGY_TX_BINDING_MISSING = "Terminology_TX_Binding_Missing"; - public final static String TERMINOLOGY_TX_CODE_VALUESET = "Terminology_TX_Code_ValueSet"; - public final static String TERMINOLOGY_TX_CODING_COUNT = "Terminology_TX_Coding_Count"; - public final static String TERMINOLOGY_TX_SYSTEM_NOTKNOWN = "Terminology_TX_System_NotKnown"; - public final static String TERMINOLOGY_TX_SYSTEM_VALUESET = "Terminology_TX_System_ValueSet"; - public final static String TERMINOLOGY_TX_SYSTEM_INVALID = "Terminology_TX_System_Invalid"; - public final static String TERMINOLOGY_TX_DISPLAY_WRONG = "Terminology_TX_Display_Wrong"; - public final static String TERMINOLOGY_TX_CODE_UNKNOWN = "Terminology_TX_Code_Unknown"; - public final static String TERMINOLOGY_TX_SYSTEM_UNKNOWN = "Terminology_TX_System_Unknown"; - public final static String TERMINOLOGY_PASSTHROUGH_TX_MESSAGE = "Terminology_PassThrough_TX_Message"; - public final static String FIXED_TYPE_CHECKS_DT_ADDRESS_LINE = "Fixed_Type_Checks_DT_Address_Line"; - public final static String MUSTSUPPORT_VAL_MUSTSUPPORT = "MustSupport_VAL_MustSupport"; public final static String ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD = "All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod"; public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index a5f5f2e3d..66b32adce 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -1,229 +1,229 @@ #InstanceValidator -Resource_RES_ID_Prohibited = Resource has an id, but none is allowed -Resource_RES_ID_Missing = Resource requires an id, but none is present -Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' -Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' -Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader -Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} -Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order -Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0} -Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} -Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} -Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} -Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} -Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} -Validation_VAL_Unknown_Profile = Unknown profile {0} -Validation_VAL_NoType = Unknown type {0} -Validation_VAL_Content_Unknown = Unrecognised Content {0} -Extension_EXT_URL_Absolute = Extension.url must be an absolute URL -Extension_EXT_Url_NotFound = Extension.url is required -Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition -Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} -Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry +Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry +Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' -Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') -Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself -Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches -Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry -Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry -Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided -Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option -Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding -Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided -Questionnaire_QR_Item_NoString = The string {0} is not a valid option -Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string -Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided -Questionnaire_QR_Item_NoTime = The time {0} is not a valid option -Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time -Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided -Questionnaire_QR_Item_NoDate = The date {0} is not a valid option -Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date -Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided -Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option -Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer -Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided -Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options -Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire -Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator -Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} -Questionnaire_QR_Item_WrongType = Answer value must be of type {0} -Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') -Questionnaire_QR_Item_Order = Structural Error: items are out of order -Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire -Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated -Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} -Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} -Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type -Questionnaire_QR_Item_Group = Items of type group should not have answers -Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed -Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} -Questionnaire_QR_Item_Missing = No response answer found for required item {0} -Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} -Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire -Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire -CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) -CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} -Meta_RES_Security_Duplicate = Duplicate Security Label {0} -Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ -Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ -Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself -Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself -Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen -Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition -Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link -Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided -Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked -Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl -Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} -Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here +Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} -Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} -Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) -Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements -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_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} -Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference -Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1}) -Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} -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_CantResolveProfile = Unable to resolve the profile reference '{0}' -Reference_REF_NoType = Unable to determine type of target resource -Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) -Reference_REF_CantResolve = Unable to resolve resource '{0}' -Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display -Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked -Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} -Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server -Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator -XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') -XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') -XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') -XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div -Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} -Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) -Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error -Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) -Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals -Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal -Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 -Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 -Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} -Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} -Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 -Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer -Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value -Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) -Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) -Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) -Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time -Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) -Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace -Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty -Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid -Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve -Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) -Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: -Type_Specific_Checks_DT_OID_Valid = OIDs must be valid -Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: -Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') -Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: -Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: -Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' -Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' -Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace -Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions -Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference -Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements -Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements -Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements -Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements -Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} -Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} -Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions -Internal_INT_Bad_Type = Unhandled fixed value type {0} -_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' -Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} -Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile -Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) -Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} -Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} +Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_URL_Absolute = Extension.url must be an absolute URL Extension_EXT_Unknown = Unknown extension {0} Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here -Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Url_NotFound = Extension.url is required Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal -Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) -Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') -Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} -Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked -Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked -Terminology_TX_Error_Coding1 = Error {0} validating Coding -Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} -Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} -Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code -Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required -Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server -Terminology_TX_Binding_Missing2 = Binding for {0} missing -Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') -Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference -Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet -Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} -Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) -Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) -Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) -Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} -Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} -Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} -Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept -Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} -Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) -Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) -Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) -Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) -Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) -Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) -Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) -Terminology_TX_Binding_Missing = Binding for {0} missing (cc) -Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) -Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements -Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated -Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system -Terminology_TX_System_Invalid = Invalid System URI: {0} -Terminology_TX_Display_Wrong = Display should be '{0}' -Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) -Terminology_TX_System_Unknown = Unknown Code System '{0}' -Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') +Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements +Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements +Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements +Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements +Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements +Internal_INT_Bad_Type = Unhandled fixed value type {0} +Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Meta_RES_Security_Duplicate = Duplicate Security Label {0} MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire +Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options +Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided +Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided +Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} +Questionnaire_QR_Item_Group = Items of type group should not have answers +Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided +Questionnaire_QR_Item_Missing = No response answer found for required item {0} +Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option +Questionnaire_QR_Item_NoDate = The date {0} is not a valid option +Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option +Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided +Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding +Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date +Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer +Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string +Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time +Questionnaire_QR_Item_NoString = The string {0} is not a valid option +Questionnaire_QR_Item_NoTime = The time {0} is not a valid option +Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type +Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') +Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed +Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} +Questionnaire_QR_Item_Order = Structural Error: items are out of order +Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided +Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} +Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided +Questionnaire_QR_Item_WrongType = Answer value must be of type {0} +Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} +Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself +Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +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_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 ULR is allowed ([type]?parameters. Encountered {0}) +Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} +Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display +Reference_REF_NoType = Unable to determine type of target resource +Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} +Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Resource_RES_ID_Missing = Resource requires an id, but none is present +Resource_RES_ID_Prohibited = Resource has an id, but none is allowed +Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked +Terminology_TX_Binding_Missing = Binding for {0} missing (cc) +Terminology_TX_Binding_Missing2 = Binding for {0} missing +Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked +Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} +Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) +Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) +Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) +Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) +Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements +Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept +Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet +Terminology_TX_Error_Coding1 = Error {0} validating Coding +Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} +Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} +Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} +Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server +Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} +Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_System_Invalid = Invalid System URI: {0} +Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference +Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator +Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator +Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time +Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) +Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) +Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal +Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference +Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) +Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} +Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} +Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 +Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 +Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: +Type_Specific_Checks_DT_OID_Valid = OIDs must be valid +Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} +Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty +Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions +Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace +Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) +Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace +Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) +Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: +Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') +Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: +Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) +Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader +Validation_VAL_Content_Unknown = Unrecognised Content {0} +Validation_VAL_NoType = Unknown type {0} +Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} +Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} +Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} +Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} +Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +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_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} +Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked +Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Unknown_Profile = Unknown profile {0} +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div +_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index a5f5f2e3d..66b32adce 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -1,229 +1,229 @@ #InstanceValidator -Resource_RES_ID_Prohibited = Resource has an id, but none is allowed -Resource_RES_ID_Missing = Resource requires an id, but none is present -Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' -Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' -Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader -Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} -Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order -Validation_VAL_Profile_NotAllowed = This element is not allowed by the profile {0} -Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} -Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} -Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} -Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} -Validation_VAL_Profile_NoMatch = Unable to find matching profile among choices: {0} -Validation_VAL_Unknown_Profile = Unknown profile {0} -Validation_VAL_NoType = Unknown type {0} -Validation_VAL_Content_Unknown = Unrecognised Content {0} -Extension_EXT_URL_Absolute = Extension.url must be an absolute URL -Extension_EXT_Url_NotFound = Extension.url is required -Validation_VAL_Profile_NoType = The type of element {0} is not known, which is illegal. Valid types at this point are {1} +Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition -Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} -Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches +Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry +Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry +Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' -Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') -Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself -Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches -Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry -Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry -Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided -Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option -Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding -Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided -Questionnaire_QR_Item_NoString = The string {0} is not a valid option -Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string -Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided -Questionnaire_QR_Item_NoTime = The time {0} is not a valid option -Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time -Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided -Questionnaire_QR_Item_NoDate = The date {0} is not a valid option -Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date -Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided -Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option -Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer -Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided -Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options -Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire -Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator -Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} -Questionnaire_QR_Item_WrongType = Answer value must be of type {0} -Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') -Questionnaire_QR_Item_Order = Structural Error: items are out of order -Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire -Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated -Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} -Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} -Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type -Questionnaire_QR_Item_Group = Items of type group should not have answers -Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed -Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} -Questionnaire_QR_Item_Missing = No response answer found for required item {0} -Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} -Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire -Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire -CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) -CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} -Meta_RES_Security_Duplicate = Duplicate Security Label {0} -Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ -Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ -Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) -Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself -Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself -Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen -Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition -Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link -Validation_VAL_Profile_NoSnapshot = StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided -Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked -Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl -Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} -Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here +Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} +Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} -Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} -Reference_REF_Format1 = Relative URLs must be of the format [ResourceName]/[id], or a search ULR is allowed ([type]?parameters. Encountered {0}) -Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl +Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements -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_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} -Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference -Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1}) -Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} -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_CantResolveProfile = Unable to resolve the profile reference '{0}' -Reference_REF_NoType = Unable to determine type of target resource -Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) -Reference_REF_CantResolve = Unable to resolve resource '{0}' -Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display -Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked -Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} -Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server -Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator -XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') -XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') -XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') -XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div -Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} -Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) -Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error -Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) -Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals -Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal -Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 -Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 -Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} -Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} -Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 -Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer -Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value -Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) -Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) -Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) -Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time -Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) -Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace -Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty -Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid -Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve -Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) -Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: -Type_Specific_Checks_DT_OID_Valid = OIDs must be valid -Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: -Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') -Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: -Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: -Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' -Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' -Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace -Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions -Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference -Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements -Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements -Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements -Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements -Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} -Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} -Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions -Internal_INT_Bad_Type = Unhandled fixed value type {0} -_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' -Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} -Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile -Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) -Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} -Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} +Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not +Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension +Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_URL_Absolute = Extension.url must be an absolute URL Extension_EXT_Unknown = Unknown extension {0} Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here -Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} +Extension_EXT_Url_NotFound = Extension.url is required Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal -Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) -Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') -Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} -Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked -Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked -Terminology_TX_Error_Coding1 = Error {0} validating Coding -Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} -Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} -Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code -Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required -Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server -Terminology_TX_Binding_Missing2 = Binding for {0} missing -Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') -Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference -Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet -Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} -Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) -Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) -Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) -Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} -Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} -Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} -Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept -Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} -Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) -Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) -Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) -Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) -Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) -Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) -Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) -Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) -Terminology_TX_Binding_Missing = Binding for {0} missing (cc) -Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) -Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements -Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated -Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system -Terminology_TX_System_Invalid = Invalid System URI: {0} -Terminology_TX_Display_Wrong = Display should be '{0}' -Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) -Terminology_TX_System_Unknown = Unknown Code System '{0}' -Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') +Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements +Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements +Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements +Fixed_Type_Checks_DT_Name_Prefix = Expected {0} but found {1} prefix elements +Fixed_Type_Checks_DT_Name_Suffix = Expected {0} but found {1} suffix elements +Internal_INT_Bad_Type = Unhandled fixed value type {0} +Language_XHTML_Lang_Different1 = Resource has a language ({0}), and the XHTML has a lang ({1}), but they differ +Language_XHTML_Lang_Different2 = Resource has a language ({0}), and the XHTML has an xml:lang ({1}), but they differ +Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not have an lang or an xml:lang tag (needs both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) +Meta_RES_Security_Duplicate = Duplicate Security Label {0} MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') +Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile +Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire +Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options +Questionnaire_QR_Item_CodingNoOptions = Cannot validate Coding option because no option list is provided +Questionnaire_QR_Item_DateNoOptions = Cannot validate date answer option because no option list is provided +Questionnaire_QR_Item_Display = Items not of type DISPLAY should not have items - linkId {0} +Questionnaire_QR_Item_Group = Items of type group should not have answers +Questionnaire_QR_Item_IntNoOptions = Cannot validate integer answer option because no option list is provided +Questionnaire_QR_Item_Missing = No response answer found for required item {0} +Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option +Questionnaire_QR_Item_NoDate = The date {0} is not a valid option +Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option +Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided +Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding +Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date +Questionnaire_QR_Item_NoOptionsInteger = Option list has no option values of type integer +Questionnaire_QR_Item_NoOptionsString = Option list has no option values of type string +Questionnaire_QR_Item_NoOptionsTime = Option list has no option values of type time +Questionnaire_QR_Item_NoString = The string {0} is not a valid option +Questionnaire_QR_Item_NoTime = The time {0} is not a valid option +Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type +Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') +Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed +Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} +Questionnaire_QR_Item_Order = Structural Error: items are out of order +Questionnaire_QR_Item_StringNoOptions = Cannot validate string answer option because no option list is provided +Questionnaire_QR_Item_Text = If text exists, it must match the questionnaire definition for linkId {0} +Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because no option list is provided +Questionnaire_QR_Item_WrongType = Answer value must be of type {0} +Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} +Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself +Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +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_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 ULR is allowed ([type]?parameters. Encountered {0}) +Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} +Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} +Reference_REF_NoDisplay = A Reference without an actual reference or identifier should have a display +Reference_REF_NoType = Unable to determine type of target resource +Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} +Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} +Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Resource_RES_ID_Missing = Resource requires an id, but none is present +Resource_RES_ID_Prohibited = Resource has an id, but none is allowed +Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked +Terminology_TX_Binding_Missing = Binding for {0} missing (cc) +Terminology_TX_Binding_Missing2 = Binding for {0} missing +Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked +Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} +Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) +Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) +Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) +Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) +Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements +Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) +Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) +Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required +Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set +Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept +Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet +Terminology_TX_Error_Coding1 = Error {0} validating Coding +Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} +Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}, and a code from this value set is required) (code = {2}#{3}) +Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2} +Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} +Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} +Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server +Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) +Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) +Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} +Terminology_TX_NoValid_5 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code{1} +Terminology_TX_NoValid_6 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set{1} +Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) +Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_System_Invalid = Invalid System URI: {0} +Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference +Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator +Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator +Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) +Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time +Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone +Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) +Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) +Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal +Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference +Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) +Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} +Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} +Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 +Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 +Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: +Type_Specific_Checks_DT_OID_Valid = OIDs must be valid +Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} +Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty +Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions +Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace +Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) +Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace +Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) +Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: +Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') +Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: +Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) +Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader +Validation_VAL_Content_Unknown = Unrecognised Content {0} +Validation_VAL_NoType = Unknown type {0} +Validation_VAL_Profile_MatchMultiple = Profile {0}, Element matches more than one slice - {1}, {2} +Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} +Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} +Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} +Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation +Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation +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_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} +Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice +Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked +Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Unknown_Profile = Unknown profile {0} +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div +_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject From ba5eea67741eabe57cc96e176482ee2b70b6a653 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 22:22:52 +0100 Subject: [PATCH 20/52] fixed MessageFormat.format for messages without arguments removed duplicage entry from property file --- .../java/org/hl7/fhir/validation/BaseValidator.java | 10 ++++++++-- .../fhir/validation/instance/InstanceValidator.java | 2 +- .../src/main/resources/Messages.properties | 1 - .../src/main/resources/Messages_de_DE.properties | 1 - 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index cc8576f70..8016e14bb 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -53,6 +53,7 @@ POSSIBILITY OF SUCH DAMAGE. import java.text.MessageFormat; import java.util.List; +import java.util.MissingResourceException; import java.util.ResourceBundle; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.Resource; @@ -150,8 +151,13 @@ public class BaseValidator { String message; if (theMessageArguments != null && theMessageArguments.length > 0) { message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); - } else { - message = theMessage; + } else { + try { + message = messages.getString(theMessage); + } catch (MissingResourceException e) { + // non-internationalized String + message = theMessage; + } } return message; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 76cb6f88e..493b66b69 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -1818,7 +1818,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat warning(errors, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue()); rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() - .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID); + .matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, e.primitiveValue()); rule(errors, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ); rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()); try { diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 66b32adce..293880d47 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -166,7 +166,6 @@ Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index 66b32adce..293880d47 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -166,7 +166,6 @@ Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_DateTIme_Valid = Not a valid date time Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone From 594d77985d038d88f1e50fade5700f6e5f2e76d5 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 22:37:34 +0100 Subject: [PATCH 21/52] extracted Throwable messages --- .../instance/InstanceValidator.java | 84 +++++++++---------- .../fhir/validation/utils/I18nConstants.java | 39 +++++++++ .../src/main/resources/Messages.properties | 39 +++++++++ .../main/resources/Messages_de_DE.properties | 39 +++++++++ 4 files changed, 159 insertions(+), 42 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 493b66b69..b0343a234 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -197,17 +197,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat @Override public FunctionDetails resolveFunction(String functionName) { - throw new Error("Not done yet (ValidatorHostServices.resolveFunction): " + functionName); + throw new Error(formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_, functionName)); } @Override public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { - throw new Error("Not done yet (ValidatorHostServices.checkFunction)"); + throw new Error(formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION)); } @Override public List executeFunction(Object appContext, String functionName, List> parameters) { - throw new Error("Not done yet (ValidatorHostServices.executeFunction)"); + throw new Error(formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION)); } @Override @@ -240,7 +240,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat throw new FHIRException(e); } else - throw new Error("Not done yet - resolve " + url + " locally (2)"); + throw new Error(formatMessage(I18nConstants.NOT_DONE_YET__RESOLVE__LOCALLY_2, url)); } @@ -269,7 +269,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ValidatorHostContext ctxt = (ValidatorHostContext) appContext; StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); if (sd == null) { - throw new FHIRException("Unable to resolve " + url); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_, url)); } InstanceValidator self = InstanceValidator.this; List valerrors = new ArrayList(); @@ -285,10 +285,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (e.isResource()) { self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(e)); } else { - throw new FHIRException("Not supported yet"); + throw new FHIRException(formatMessage(I18nConstants.NOT_SUPPORTED_YET)); } } else - throw new NotImplementedException("Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element"); + throw new NotImplementedException(formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT)); boolean ok = true; List record = new ArrayList<>(); for (ValidationMessage v : valerrors) { @@ -312,7 +312,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (r instanceof ValueSet) return (ValueSet) r; else - throw new FHIRException("Reference " + url + " refers to a " + r.fhirType() + " not a ValueSet"); + throw new FHIRException(formatMessage(I18nConstants.REFERENCE__REFERS_TO_A__NOT_A_VALUESET, url, r.fhirType())); } } return null; @@ -513,7 +513,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private StructureDefinition getSpecifiedProfile(String profile) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, profile); if (sd == null) { - throw new FHIRException("Unable to locate the profile '" + profile + "' in order to validate against it"); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT, profile)); } return sd; } @@ -1566,7 +1566,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = true; } } else { - throw new Error("Unrecognised extension context " + ctxt.getTypeElement().asStringValue()); + throw new Error(formatMessage(I18nConstants.UNRECOGNISED_EXTENSION_CONTEXT_, ctxt.getTypeElement().asStringValue())); } } if (!ok) { @@ -2092,7 +2092,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (we == null) { if (fetcher == null) { if (!refType.equals("contained")) - throw new FHIRException("Resource resolution services not provided"); + throw new FHIRException(formatMessage(I18nConstants.RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED)); } else { Element ext = null; if (fetchCache.containsKey(ref)) { @@ -2477,7 +2477,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String id = p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT) ? p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT) : null; StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); if (sd == null) - throw new DefinitionException("Unable to resolve profile " + p); + throw new DefinitionException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_PROFILE_, p)); profile = sd; if (id == null) element = sd.getSnapshot().getElementFirstRep(); @@ -2488,7 +2488,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat element = t; } if (element == null) - throw new DefinitionException("Unable to resolve element " + id + " in profile " + p); + throw new DefinitionException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_, id, p)); } expr = fpe.parse(fixExpr(discriminator)); t2 = System.nanoTime(); @@ -2639,20 +2639,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (focus.fhirType().equals("Reference") && d.equals("reference")) { String url = focus.getChildValue("reference"); if (Utilities.noString(url)) - throw new FHIRException("No reference resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException(formatMessage(I18nConstants.NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_, discriminator, element.getProperty().getName())); // Note that we use the passed in stack here. This might be a problem if the discriminator is deep enough? Element target = resolve(appContext, url, stack, errors, p); if (target == null) - throw new FHIRException("Unable to find resource " + url + " at " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_, url, d, discriminator, element.getProperty().getName())); focus = target; } else if (d.equals("value") && focus.isPrimitive()) { return focus; } else { List children = focus.getChildren(d); if (children.isEmpty()) - throw new FHIRException("Unable to find " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_, d, discriminator, element.getProperty().getName())); if (children.size() > 1) - throw new FHIRException("Found " + Integer.toString(children.size()) + " items for " + d + " resolving discriminator " + discriminator + " from " + element.getProperty().getName()); + throw new FHIRException(formatMessage(I18nConstants.FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_, Integer.toString(children.size()), d, discriminator, element.getProperty().getName())); focus = children.get(0); p = p + "." + d; } @@ -3077,23 +3077,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat discriminator = discriminator.substring(0, discriminator.indexOf('[')); type = criteriaElement.getType().get(0).getWorkingCode(); } else if (criteriaElement.getType().size() > 1) { - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has multiple types: " + criteriaElement.typeSummary()); + throw new DefinitionException(formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_, discriminator, ed.getId(), profile.getUrl(), criteriaElement.typeSummary())); } else - throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + ed.getId() + " in " + profile.getUrl() + " has no types"); + throw new DefinitionException(formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getUrl())); if (discriminator.isEmpty()) expression.append(" and $this is " + type); else expression.append(" and " + discriminator + " is " + type); } else if (s.getType() == DiscriminatorType.PROFILE) { if (criteriaElement.getType().size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException(formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl())); } if (criteriaElement.getType().size() != 1) { - throw new DefinitionException("Profile based discriminators must have only one type (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException(formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl())); } List list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile(); if (list.size() == 0) { - throw new DefinitionException("Profile based discriminators must have a type with a profile (" + criteriaElement.getId() + " in profile " + profile.getUrl() + ")"); + throw new DefinitionException(formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl())); } else if (list.size() > 1) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" or "); for (CanonicalType c : list) { @@ -3109,7 +3109,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) expression.append(" and (" + discriminator + ".exists().not())"); else - throw new FHIRException("Discriminator (" + discriminator + ") is based on element existence, but slice " + ed.getId() + " neither sets min>=1 or max=0"); + throw new FHIRException(formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0, discriminator, ed.getId())); } else if (criteriaElement.hasFixed()) { buildFixedExpression(ed, expression, discriminator, criteriaElement); } else if (criteriaElement.hasPattern()) { @@ -3127,15 +3127,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (!anyFound) { if (slicer.getSlicing().getDiscriminator().size() > 1) - throw new DefinitionException("Could not match any discriminators (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - None of the discriminator " + discriminators + " have fixed value, binding or existence assertions"); + throw new DefinitionException(formatMessage(I18nConstants.COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators)); else - throw new DefinitionException("Could not match discriminator (" + discriminators + ") for slice " + ed.getId() + " in profile " + profile.getUrl() + " - the discriminator " + discriminators + " does not have fixed value, binding or existence assertions"); + throw new DefinitionException(formatMessage(I18nConstants.COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS, discriminators, ed.getId(), profile.getUrl(), discriminators)); } try { n = fpe.parse(fixExpr(expression.toString())); } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + expression + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + throw new FHIRException(formatMessage(I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, expression, profile.getUrl(), path, e.getMessage())); } fpeTime = fpeTime + (System.nanoTime() - t); ed.setUserData("slice.expression.cache", n); @@ -3163,7 +3163,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat msg = fpe.forLog(); } catch (Exception ex) { ex.printStackTrace(); - throw new FHIRException("Problem evaluating slicing expression for element in profile " + profile.getUrl() + " path " + path + " (fhirPath = " + n + "): " + ex.getMessage()); + throw new FHIRException(formatMessage(I18nConstants.PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___, profile.getUrl(), path, n, ex.getMessage())); } return ok; } @@ -3183,13 +3183,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat expression.append(" and "); buildIdentifierExpression(ed, expression, discriminator, ii); } else - throw new DefinitionException("Unsupported fixed pattern type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + pattern.getClass().getName()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), pattern.getClass().getName())); } private void buildIdentifierExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Identifier ii) throws DefinitionException { if (ii.hasExtension()) - throw new DefinitionException("Unsupported Identifier pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); boolean first = true; expression.append(discriminator + ".where("); if (ii.hasSystem()) { @@ -3223,15 +3223,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodeableConceptExpression(ElementDefinition ed, StringBuilder expression, String discriminator, CodeableConcept cc) throws DefinitionException { if (cc.hasText()) - throw new DefinitionException("Unsupported CodeableConcept pattern - using text - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); if (!cc.hasCoding()) - throw new DefinitionException("Unsupported CodeableConcept pattern - must have at least one coding - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); if (cc.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); boolean firstCoding = true; for (Coding c : cc.getCoding()) { if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); if (firstCoding) firstCoding = false; else expression.append(" and "); expression.append(discriminator + ".coding.where("); @@ -3262,7 +3262,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void buildCodingExpression(ElementDefinition ed, StringBuilder expression, String discriminator, Coding c) throws DefinitionException { if (c.hasExtension()) - throw new DefinitionException("Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator(" + discriminator + ") for slice " + ed.getId()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_, discriminator, ed.getId())); expression.append(discriminator + ".where("); boolean first = true; if (c.hasSystem()) { @@ -3318,7 +3318,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (fixed instanceof BooleanType) { expression.append(((BooleanType) fixed).asStringValue()); } else - throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + ed.getId() + ": " + fixed.getClass().getName()); + throw new DefinitionException(formatMessage(I18nConstants.UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__, discriminator, ed.getId(), fixed.getClass().getName())); expression.append(" in " + discriminator + ")"); } } @@ -3680,7 +3680,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat byte[] json = bs.toByteArray(); switch (v) { case DSTU1: - throw new FHIRException("Unsupported version R1"); + throw new FHIRException(formatMessage(I18nConstants.UNSUPPORTED_VERSION_R1)); case DSTU2: org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); Resource r5 = VersionConvertor_10_50.convertResource(r2); @@ -4622,7 +4622,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else dt = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + actualType); if (dt == null) - throw new DefinitionException("Unable to resolve actual type " + actualType); + throw new DefinitionException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType)); trackUsage(dt, hostContext, element); childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); @@ -4980,7 +4980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String errorContext = "profile " + profile.getUrl(); if (!resource.getChildValue("id").isEmpty()) errorContext += "; instance " + resource.getChildValue("id"); - throw new DefinitionException("Slice encountered midway through set (path = " + slicer.getPath() + ", id = " + slicer.getId() + "); " + errorContext); + throw new DefinitionException(formatMessage(I18nConstants.SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___, slicer.getPath(), slicer.getId(), errorContext)); } slicer = ed; process = false; @@ -5104,7 +5104,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (tail.equals(t.getId())) return t; } - throw new DefinitionException("Unable to find element with id '" + tail + "'"); + throw new DefinitionException(formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT_WITH_ID_, tail)); } private IdStatus idStatusForEntry(Element ep, ElementInfo ei) { @@ -5176,7 +5176,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { n = fpe.parse(fixExpr(inv.getExpression())); } catch (FHIRLexerException e) { - throw new FHIRException("Problem processing expression " + inv.getExpression() + " in profile " + profile.getUrl() + " path " + path + ": " + e.getMessage()); + throw new FHIRException(formatMessage(I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage())); } fpeTime = fpeTime + (System.nanoTime() - t); inv.setUserData("validator.expression.cache", n); @@ -5315,9 +5315,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getUrl(), "value", null); return msgs.size() == 0; } else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) { - throw new FHIRException("Unable to resolve slice matching - slice matching by value set not done"); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE)); } else { - throw new FHIRException("Unable to resolve slice matching - no fixed value or required value set"); + throw new FHIRException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET)); } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java index 934f60099..2a30f494c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/utils/I18nConstants.java @@ -229,5 +229,44 @@ public class I18nConstants { public final static String ALL_OBSERVATIONS_SHOULD_HAVE_AN_EFFECTIVEDATETIME_OR_AN_EFFECTIVEPERIOD = "All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod"; public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_PERFORMER = "All_observations_should_have_a_performer"; public final static String ALL_OBSERVATIONS_SHOULD_HAVE_A_SUBJECT = "All_observations_should_have_a_subject"; + public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__NO_FIXED_VALUE_OR_REQUIRED_VALUE_SET = "Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set"; + public final static String UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE = "Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done"; + public final static String PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__ = "Problem_processing_expression__in_profile__path__"; + public final static String UNABLE_TO_FIND_ELEMENT_WITH_ID_ = "Unable_to_find_element_with_id_"; + public final static String SLICE_ENCOUNTERED_MIDWAY_THROUGH_SET_PATH___ID___ = "Slice_encountered_midway_through_set_path___id___"; + public final static String UNABLE_TO_RESOLVE_ACTUAL_TYPE_ = "Unable_to_resolve_actual_type_"; + public final static String UNSUPPORTED_VERSION_R1 = "Unsupported_version_R1"; + public final static String UNSUPPORTED_FIXED_VALUE_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_value_type_for_discriminator_for_slice__"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__MUST_HAVE_AT_LEAST_ONE_CODING__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_CODEABLECONCEPT_PATTERN__USING_TEXT__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_IDENTIFIER_PATTERN__EXTENSIONS_ARE_NOT_ALLOWED__FOR_DISCRIMINATOR_FOR_SLICE_ = "Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_"; + public final static String UNSUPPORTED_FIXED_PATTERN_TYPE_FOR_DISCRIMINATOR_FOR_SLICE__ = "Unsupported_fixed_pattern_type_for_discriminator_for_slice__"; + public final static String PROBLEM_EVALUATING_SLICING_EXPRESSION_FOR_ELEMENT_IN_PROFILE__PATH__FHIRPATH___ = "Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___"; + public final static String COULD_NOT_MATCH_DISCRIMINATOR__FOR_SLICE__IN_PROFILE___THE_DISCRIMINATOR__DOES_NOT_HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions"; + public final static String COULD_NOT_MATCH_ANY_DISCRIMINATORS__FOR_SLICE__IN_PROFILE___NONE_OF_THE_DISCRIMINATOR__HAVE_FIXED_VALUE_BINDING_OR_EXISTENCE_ASSERTIONS = "Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions"; + public final static String DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0 = "Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_only_one_type__in_profile_"; + public final static String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type__in_profile_"; + public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES = "Discriminator__is_based_on_type_but_slice__in__has_no_types"; + public final static String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_ = "Discriminator__is_based_on_type_but_slice__in__has_multiple_types_"; + public final static String FOUND__ITEMS_FOR__RESOLVING_DISCRIMINATOR__FROM_ = "Found__items_for__resolving_discriminator__from_"; + public final static String UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find__resolving_discriminator__from_"; + public final static String UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find_resource__at__resolving_discriminator__from_"; + public final static String NO_REFERENCE_RESOLVING_DISCRIMINATOR__FROM_ = "No_reference_resolving_discriminator__from_"; + public final static String UNABLE_TO_RESOLVE_ELEMENT__IN_PROFILE_ = "Unable_to_resolve_element__in_profile_"; + public final static String UNABLE_TO_RESOLVE_PROFILE_ = "Unable_to_resolve_profile_"; + public final static String RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED = "Resource_resolution_services_not_provided"; + public final static String UNRECOGNISED_EXTENSION_CONTEXT_ = "Unrecognised_extension_context_"; + public final static String UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT = "Unable_to_locate_the_profile__in_order_to_validate_against_it"; + public final static String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT = "Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element"; + public final static String NOT_SUPPORTED_YET = "Not_supported_yet"; + public final static String UNABLE_TO_RESOLVE_ = "Unable_to_resolve_"; + public final static String NOT_DONE_YET__RESOLVE__LOCALLY_2 = "Not_done_yet__resolve__locally_2"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION = "Not_done_yet_ValidatorHostServicesexecuteFunction"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESCHECKFUNCTION = "Not_done_yet_ValidatorHostServicescheckFunction"; + public final static String NOT_DONE_YET_VALIDATORHOSTSERVICESRESOLVEFUNCTION_ = "Not_done_yet_ValidatorHostServicesresolveFunction_"; } diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 293880d47..001b8b057 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -226,3 +226,42 @@ _DT_Fixed_Wrong = Value is '{0}' but must be '{1}' All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject +Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set +Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done +Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} +Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} +Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} +Unsupported_version_R1 = Unsupported version R1 +Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} +Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} +Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} +Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} +Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions +Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions +Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 +Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) +Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) +Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) +Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types +Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} +Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} +Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} +Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} +No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} +Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} +Unable_to_resolve_profile_ = Unable to resolve profile {0} +Resource_resolution_services_not_provided = Resource resolution services not provided +Unrecognised_extension_context_ = Unrecognised extension context {0} +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet +Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element +Not_supported_yet = Not supported yet +Unable_to_resolve_ = Unable to resolve {0} +Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) +Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) +Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) +Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index 293880d47..001b8b057 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -226,3 +226,42 @@ _DT_Fixed_Wrong = Value is '{0}' but must be '{1}' All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject +Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set +Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done +Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} +Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} +Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} +Unsupported_version_R1 = Unsupported version R1 +Unsupported_fixed_value_type_for_discriminator_for_slice__ = Unsupported fixed value type for discriminator({0}) for slice {1}: {2} +Unsupported_CodeableConcept_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__must_have_at_least_one_coding__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - must have at least one coding - for discriminator({0}) for slice {1} +Unsupported_CodeableConcept_pattern__using_text__for_discriminator_for_slice_ = Unsupported CodeableConcept pattern - using text - for discriminator({0}) for slice {1} +Unsupported_Identifier_pattern__extensions_are_not_allowed__for_discriminator_for_slice_ = Unsupported Identifier pattern - extensions are not allowed - for discriminator({0}) for slice {1} +Unsupported_fixed_pattern_type_for_discriminator_for_slice__ = Unsupported fixed pattern type for discriminator({0}) for slice {1}: {2} +Problem_evaluating_slicing_expression_for_element_in_profile__path__fhirPath___ = Problem evaluating slicing expression for element in profile {0} path {1} (fhirPath = {2}): {3} +Could_not_match_discriminator__for_slice__in_profile___the_discriminator__does_not_have_fixed_value_binding_or_existence_assertions = Could not match discriminator ({0}) for slice {1} in profile {2} - the discriminator {3} does not have fixed value, binding or existence assertions +Could_not_match_any_discriminators__for_slice__in_profile___None_of_the_discriminator__have_fixed_value_binding_or_existence_assertions = Could not match any discriminators ({0}) for slice {1} in profile {2} - None of the discriminator {3} have fixed value, binding or existence assertions +Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0 +Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1}) +Profile_based_discriminators_must_have_only_one_type__in_profile_ = Profile based discriminators must have only one type ({0} in profile {1}) +Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1}) +Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types +Discriminator__is_based_on_type_but_slice__in__has_multiple_types_ = Discriminator ({0}) is based on type, but slice {1} in {2} has multiple types: {3} +Found__items_for__resolving_discriminator__from_ = Found {0} items for {1} resolving discriminator {2} from {3} +Unable_to_find__resolving_discriminator__from_ = Unable to find {0} resolving discriminator {1} from {2} +Unable_to_find_resource__at__resolving_discriminator__from_ = Unable to find resource {0} at {1} resolving discriminator {2} from {3} +No_reference_resolving_discriminator__from_ = No reference resolving discriminator {0} from {1} +Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profile {1} +Unable_to_resolve_profile_ = Unable to resolve profile {0} +Resource_resolution_services_not_provided = Resource resolution services not provided +Unrecognised_extension_context_ = Unrecognised extension context {0} +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet +Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element +Not_supported_yet = Not supported yet +Unable_to_resolve_ = Unable to resolve {0} +Not_done_yet__resolve__locally_2 = Not done yet - resolve {0} locally (2) +Not_done_yet_ValidatorHostServicesexecuteFunction = Not done yet (ValidatorHostServices.executeFunction) +Not_done_yet_ValidatorHostServicescheckFunction = Not done yet (ValidatorHostServices.checkFunction) +Not_done_yet_ValidatorHostServicesresolveFunction_ = Not done yet (ValidatorHostServices.resolveFunction): {0} From 822b50b8d7ab19ad56d9806cff8d144250226d32 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 22:44:28 +0100 Subject: [PATCH 22/52] replaced Exception controll flow --- .../java/org/hl7/fhir/validation/BaseValidator.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 8016e14bb..ce35adbc3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -146,18 +146,15 @@ public class BaseValidator { return thePass; } - + protected String formatMessage(String theMessage, Object... theMessageArguments) { String message; if (theMessageArguments != null && theMessageArguments.length > 0) { message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); + } else if (messages.containsKey(theMessage)) { + message = messages.getString(theMessage); } else { - try { - message = messages.getString(theMessage); - } catch (MissingResourceException e) { - // non-internationalized String - message = theMessage; - } + message = theMessage; } return message; } From ca6d92c39083fbb66554abe957d5c6d2fa84faee Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 22:47:41 +0100 Subject: [PATCH 23/52] deleted unnecessary imports --- .../src/main/java/org/hl7/fhir/validation/BaseValidator.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index ce35adbc3..e24153be8 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -52,11 +52,8 @@ POSSIBILITY OF SUCH DAMAGE. import java.text.MessageFormat; import java.util.List; - -import java.util.MissingResourceException; import java.util.ResourceBundle; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; From b626f5867bbd2bfbfd15598675ce64b4823f1b79 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Sun, 1 Mar 2020 22:51:57 +0100 Subject: [PATCH 24/52] always check for contained key --- .../java/org/hl7/fhir/validation/BaseValidator.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index e24153be8..c95e1f364 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -145,11 +145,13 @@ public class BaseValidator { protected String formatMessage(String theMessage, Object... theMessageArguments) { - String message; - if (theMessageArguments != null && theMessageArguments.length > 0) { - message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); - } else if (messages.containsKey(theMessage)) { - message = messages.getString(theMessage); + String message = ""; + if (messages.containsKey(theMessage)) { + if (theMessageArguments != null && theMessageArguments.length > 0) { + message = MessageFormat.format(messages.getString(theMessage), theMessageArguments); + } else if (messages.containsKey(theMessage)) { + message = messages.getString(theMessage); + } } else { message = theMessage; } From bc9ad8d608c052f40fea8f77802c838fbf9331d9 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Mar 2020 09:00:57 +1100 Subject: [PATCH 25/52] fix failing tests --- .../validation/tests/ValidationTestSuite.java | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 373c9cacb..209954362 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -75,6 +75,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour private String name; private JsonObject content; + private String version; public ValidationTestSuite(String name, JsonObject content) { this.name = name; @@ -95,33 +96,33 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (content.has("txLog")) { txLog = content.get("txLog").getAsString(); } - String v = "5.0"; + version = "5.0"; List messages = new ArrayList(); if (content.has("version")) { - v = content.get("version").getAsString(); + version = content.get("version").getAsString(); } - v = VersionUtilities.getMajMin(v); - if (!ve.containsKey(v)) { - if (v.startsWith("5.0")) - ve.put(v, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, txLog, FhirPublication.R5, true)); - else if (v.startsWith("3.0")) - ve.put(v, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, txLog, FhirPublication.STU3, true)); - else if (v.startsWith("4.0")) - ve.put(v, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, txLog, FhirPublication.R4, true)); - else if (v.startsWith("1.0")) - ve.put(v, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, txLog, FhirPublication.DSTU2, true)); - else if (v.startsWith("1.4")) - ve.put(v, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, txLog, FhirPublication.DSTU2016May, true)); + version = VersionUtilities.getMajMin(version); + if (!ve.containsKey(version)) { + if (version.startsWith("5.0")) + ve.put(version, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, txLog, FhirPublication.R5, true)); + else if (version.startsWith("3.0")) + ve.put(version, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, txLog, FhirPublication.STU3, true)); + else if (version.startsWith("4.0")) + ve.put(version, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, txLog, FhirPublication.R4, true)); + else if (version.startsWith("1.0")) + ve.put(version, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, txLog, FhirPublication.DSTU2, true)); + else if (version.startsWith("1.4")) + ve.put(version, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, txLog, FhirPublication.DSTU2016May, true)); else - throw new Exception("unknown version "+v); + throw new Exception("unknown version "+version); } - vCurr = ve.get(v); + vCurr = ve.get(version); vCurr.setFetcher(this); if (TestingUtilities.fcontexts == null) { TestingUtilities.fcontexts = new HashMap<>(); } - TestingUtilities.fcontexts.put(v, vCurr.getContext()); + TestingUtilities.fcontexts.put(version, vCurr.getContext()); if (content.has("use-test") && !content.get("use-test").getAsBoolean()) return; @@ -143,13 +144,13 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (content.has("questionnaire")) { String filename = content.get("questionnaire").getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - vCurr.getContext().cacheResource(loadResource(filename, contents, v)); + vCurr.getContext().cacheResource(loadResource(filename, contents)); } if (content.has("codesystems")) { for (JsonElement je : content.getAsJsonArray("codesystems")) { String filename = je.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - CodeSystem sd = (CodeSystem) loadResource(filename, contents, v); + CodeSystem sd = (CodeSystem) loadResource(filename, contents); val.getContext().cacheResource(sd); } } @@ -157,7 +158,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour for (JsonElement je : content.getAsJsonArray("valuesets")) { String filename = je.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - ValueSet vs = (ValueSet) loadResource(filename, contents, v); + ValueSet vs = (ValueSet) loadResource(filename, contents); val.getContext().cacheResource(vs); } } @@ -165,7 +166,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour for (JsonElement je : content.getAsJsonArray("profiles")) { String filename = je.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - StructureDefinition sd = loadProfile(filename, contents, v, messages); + StructureDefinition sd = loadProfile(filename, contents, messages); val.getContext().cacheResource(sd); } } @@ -194,15 +195,15 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour for (JsonElement e : profile.getAsJsonArray("supporting")) { String filename = e.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - CanonicalResource mr = (CanonicalResource) loadResource(filename, contents, v); + CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); val.getContext().cacheResource(mr); } } String filename = profile.get("source").getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); System.out.println("Name: " + name+" - profile : "+profile.get("source").getAsString()); - v = content.has("version") ? content.get("version").getAsString() : Constants.VERSION; - StructureDefinition sd = loadProfile(filename, contents, v, messages); + version = content.has("version") ? content.get("version").getAsString() : Constants.VERSION; + StructureDefinition sd = loadProfile(filename, contents, messages); val.getContext().cacheResource(sd); val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false); List errorsProfile = new ArrayList(); @@ -219,7 +220,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour for (JsonElement e : logical.getAsJsonArray("supporting")) { String filename = e.getAsString(); String contents = TestingUtilities.loadTestResource("validator", filename); - CanonicalResource mr = (CanonicalResource) loadResource(filename, contents, v); + CanonicalResource mr = (CanonicalResource) loadResource(filename, contents); if (mr instanceof StructureDefinition) { val.getContext().generateSnapshot((StructureDefinition) mr, true); } @@ -245,11 +246,11 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour return res; } - public StructureDefinition loadProfile(String filename, String contents, String v, List messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { - StructureDefinition sd = (StructureDefinition) loadResource(filename, contents, v); - ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(), messages, null); + public StructureDefinition loadProfile(String filename, String contents, List messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { + StructureDefinition sd = (StructureDefinition) loadResource(filename, contents); + ProfileUtilities pu = new ProfileUtilities(TestingUtilities.context(version), messages, null); if (!sd.hasSnapshot()) { - StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, sd.getBaseDefinition()); + StructureDefinition base = TestingUtilities.context(version).fetchResource(StructureDefinition.class, sd.getBaseDefinition()); pu.generateSnapshot(base, sd, sd.getUrl(), null, sd.getTitle()); // (debugging) new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", sd.getId()+".xml")), sd); } @@ -257,7 +258,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (r instanceof StructureDefinition) { StructureDefinition childSd = (StructureDefinition)r; if (!childSd.hasSnapshot()) { - StructureDefinition base = TestingUtilities.context().fetchResource(StructureDefinition.class, childSd.getBaseDefinition()); + StructureDefinition base = TestingUtilities.context(version).fetchResource(StructureDefinition.class, childSd.getBaseDefinition()); pu.generateSnapshot(base, childSd, childSd.getUrl(), null, childSd.getTitle()); } } @@ -265,34 +266,34 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour return sd; } - public Resource loadResource(String filename, String contents, String v) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { + public Resource loadResource(String filename, String contents) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) { if (filename.contains(".json")) { - if (Constants.VERSION.equals(v) || "5.0".equals(v)) + if (Constants.VERSION.equals(version) || "5.0".equals(version)) return new JsonParser().parse(inputStream); - else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(v) || "3.0".equals(v)) + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) return VersionConvertor_30_50.convertResource(new org.hl7.fhir.dstu3.formats.JsonParser().parse(inputStream), false); - else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(v) || "1.4".equals(v)) + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) return VersionConvertor_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(inputStream)); - else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(v) || "1.0".equals(v)) + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) return VersionConvertor_10_50.convertResource(new org.hl7.fhir.dstu2.formats.JsonParser().parse(inputStream)); - else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(v) || "4.0".equals(v)) + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) return VersionConvertor_40_50.convertResource(new org.hl7.fhir.r4.formats.JsonParser().parse(inputStream)); else - throw new FHIRException("unknown version "+v); + throw new FHIRException("unknown version "+version); } else { - if (Constants.VERSION.equals(v) || "5.0".equals(v)) + if (Constants.VERSION.equals(version) || "5.0".equals(version)) return new XmlParser().parse(inputStream); - else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(v) || "3.0".equals(v)) + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) return VersionConvertor_30_50.convertResource(new org.hl7.fhir.dstu3.formats.XmlParser().parse(inputStream), false); - else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(v) || "1.4".equals(v)) + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) return VersionConvertor_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.XmlParser().parse(inputStream)); - else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(v) || "1.0".equals(v)) + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) return VersionConvertor_10_50.convertResource(new org.hl7.fhir.dstu2.formats.XmlParser().parse(inputStream)); - else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(v) || "4.0".equals(v)) + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) return VersionConvertor_40_50.convertResource(new org.hl7.fhir.r4.formats.XmlParser().parse(inputStream)); else - throw new FHIRException("unknown version " + v); + throw new FHIRException("unknown version " + version); } } } @@ -320,7 +321,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour } } } - if (TestingUtilities.context().isNoTerminologyServer() || !focus.has("tx-dependent")) { + if (TestingUtilities.context(version).isNoTerminologyServer() || !focus.has("tx-dependent")) { Assert.assertEquals("Expected "+Integer.toString(java.get("errorCount").getAsInt())+" errors, but found "+Integer.toString(ec)+".", java.get("errorCount").getAsInt(), ec); if (java.has("warningCount")) Assert.assertEquals("Expected "+Integer.toString(java.get("warningCount").getAsInt())+" warnings, but found "+Integer.toString(wc)+".", java.get("warningCount").getAsInt(), wc); @@ -392,18 +393,18 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour public Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException, FHIRException { Element res = null; if (url.equals("Patient/test")) { - res = new ObjectConverter(TestingUtilities.context()).convert(new Patient()); + res = new ObjectConverter(TestingUtilities.context(version)).convert(new Patient()); } else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".json")) { - res = Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".json")); + res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".json")); } else if (TestingUtilities.findTestResource("validator", url.replace("/", "-").toLowerCase()+".xml")) { - res = Manager.makeParser(TestingUtilities.context(), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".xml")); + res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", url.replace("/", "-").toLowerCase()+".xml")); } if (res == null && url.contains("/")) { String tail = url.substring(url.indexOf("/")+1); if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase()+".json")) { - res = Manager.makeParser(TestingUtilities.context(), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".json")); + res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.JSON).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".json")); } else if (TestingUtilities.findTestResource("validator", tail.replace("/", "-").toLowerCase()+".xml")) { - res = Manager.makeParser(TestingUtilities.context(), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".xml")); + res = Manager.makeParser(TestingUtilities.context(version), FhirFormat.XML).parse(TestingUtilities.loadTestResourceStream("validator", tail.replace("/", "-").toLowerCase()+".xml")); } } return res; @@ -424,7 +425,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour @Override public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { - IResourceValidator val = TestingUtilities.context().newValidator(); + IResourceValidator val = TestingUtilities.context(version).newValidator(); List valerrors = new ArrayList(); if (item instanceof Resource) { val.validate(appContext, valerrors, (Resource) item, url); From 7f4a0f60fce3524dfe283750db00915ae7ed56a3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Mar 2020 09:02:02 +1100 Subject: [PATCH 26/52] Allow "-" in package names + fix bug scanning root folder --- .../main/java/org/hl7/fhir/utilities/cache/NpmPackage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index e1ecfedbc..e2abec238 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -77,7 +77,7 @@ import com.google.gson.JsonObject; public class NpmPackage { public static boolean isValidName(String pid) { - return pid.matches("^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)+$"); + return pid.matches("^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9\\-]*)+$"); } public static boolean isValidVersion(String ver) { @@ -826,7 +826,7 @@ public class NpmPackage { public void loadAllFiles() throws IOException { for (String folder : folders.keySet()) { NpmPackageFolder pf = folders.get(folder); - String p = Utilities.path(path, folder); + String p = folder.contains("$") ? path : Utilities.path(path, folder); for (File f : new File(p).listFiles()) { if (!f.isDirectory()) { pf.getContent().put(f.getName(), TextFile.fileToBytes(f)); From f5e2d3405ddba64b4f01e6e308749da002afd304 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Mar 2020 09:02:52 +1100 Subject: [PATCH 27/52] Add convenience write methods --- .../utilities/json/JsonTrackingParser.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java index 308710931..ac582308b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java @@ -32,6 +32,8 @@ import java.util.Stack; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; @@ -618,6 +620,23 @@ public class JsonTrackingParser { public void setErrorOnDuplicates(boolean errorOnDuplicates) { this.errorOnDuplicates = errorOnDuplicates; } - + + public static void write(JsonObject json, File file) throws IOException { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String jcnt = gson.toJson(json); + TextFile.stringToFile(jcnt, file); + } + + public static void write(JsonObject json, String fileName) throws IOException { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String jcnt = gson.toJson(json); + TextFile.stringToFile(jcnt, fileName); + } + + public static String write(JsonObject json) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + return gson.toJson(json); + } + } From 137506f37f585c49cd92f9b79e1d2289fc9600e4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Mar 2020 09:04:16 +1100 Subject: [PATCH 28/52] set id when parsing StructureMap --- .../hl7/fhir/dstu2016may/utils/StructureMapUtilities.java | 8 +++++++- .../org/hl7/fhir/dstu3/utils/StructureMapUtilities.java | 1 + .../java/org/hl7/fhir/r4/utils/StructureMapUtilities.java | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.dstu2016may/src/main/java/org/hl7/fhir/dstu2016may/utils/StructureMapUtilities.java b/org.hl7.fhir.dstu2016may/src/main/java/org/hl7/fhir/dstu2016may/utils/StructureMapUtilities.java index c2e91026a..3682d7ec0 100644 --- a/org.hl7.fhir.dstu2016may/src/main/java/org/hl7/fhir/dstu2016may/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.dstu2016may/src/main/java/org/hl7/fhir/dstu2016may/utils/StructureMapUtilities.java @@ -326,6 +326,7 @@ public class StructureMapUtilities { lexer.token("map"); StructureMap result = new StructureMap(); result.setUrl(lexer.readConstant("url")); + result.setId(tail(result.getUrl())); lexer.token("="); result.setName(lexer.readConstant("name")); lexer.skipComments(); @@ -344,10 +345,15 @@ public class StructureMapUtilities { parseGroup(result, lexer); } + return result; } - private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException { + private String tail(String url) { + return url.substring(url.lastIndexOf("/")+1); + } + + private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException { lexer.token("conceptmap"); ConceptMap map = new ConceptMap(); String id = lexer.readConstant("map id"); diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java index 627c928c4..8144f5752 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java @@ -655,6 +655,7 @@ public class StructureMapUtilities { lexer.token("map"); StructureMap result = new StructureMap(); result.setUrl(lexer.readConstant("url")); + result.setId(tail(result.getUrl())); lexer.token("="); result.setName(lexer.readConstant("name")); lexer.skipComments(); diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java index 47157315d..31f03b163 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java @@ -712,6 +712,7 @@ public class StructureMapUtilities { lexer.token("map"); StructureMap result = new StructureMap(); result.setUrl(lexer.readConstant("url")); + result.setId(tail(result.getUrl())); lexer.token("="); result.setName(lexer.readConstant("name")); lexer.skipComments(); From 1fd35f8423a2c450a2f934562c1cd5801e1210cf Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Mon, 2 Mar 2020 00:00:56 +0100 Subject: [PATCH 29/52] removed escape chars --- .../src/main/resources/Messages.properties | 120 +++++++++--------- .../main/resources/Messages_de_DE.properties | 120 +++++++++--------- 2 files changed, 120 insertions(+), 120 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 001b8b057..11f2ce91b 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -1,23 +1,23 @@ #InstanceValidator Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition -Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ("{0}" vs "{1}") Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl -Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' -Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) +Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type "{0}" +Bundle_BUNDLE_Entry_NotFound = Can't find "{0}" in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here -Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} -Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_Entry_Type = The type "{0}" is not valid - no resources allowed here +Bundle_BUNDLE_Entry_Type2 = The type "{0}" is not valid - must be {1} +Bundle_BUNDLE_Entry_Type3 = The type "{0}" is not valid - must be one of {1} Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements -CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2} CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion @@ -28,19 +28,19 @@ Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extensio Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension -Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} -Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension "{0}" must be used as a modifierExtension +Extension_EXT_Simple = The Extension "{0}" definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_SubExtension_Invalid = Sub-extension url "{0}" is not defined by the Extension {1} +Extension_EXT_Type = The Extension "{0}" definition allows for the types {1} but found type {2} Extension_EXT_URL_Absolute = Extension.url must be an absolute URL Extension_EXT_Unknown = Unknown extension {0} Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here Extension_EXT_Url_NotFound = Extension.url is required -Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal -Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') -Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') -Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_EXT_Version_Internal = Extension url "{0}" evaluation state illegal +Extension_EXT_Version_Invalid = Extension url "{0}" is not valid (invalid Version "{1}") +Extension_EXT_Version_InvalidId = Extension url "{0}" is not valid (invalid Element id "{1}") +Extension_EXT_Version_NoChange = Extension url "{0}" is not valid (Element id "{1}" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements @@ -54,8 +54,8 @@ Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not h Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Meta_RES_Security_Duplicate = Duplicate Security Label {0} MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') -Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant "{1}") +Profile_VAL_MissingElement = Missing element "{0}" - required by fixed value assigned in profile {1} Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options @@ -79,8 +79,8 @@ Questionnaire_QR_Item_NoString = The string {0} is not a valid option Questionnaire_QR_Item_NoTime = The time {0} is not a valid option Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} -Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') -Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = "{0}") +Questionnaire_QR_Item_NotFound = LinkId "{0}" not found in questionnaire Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} Questionnaire_QR_Item_Order = Structural Error: items are out of order @@ -90,19 +90,19 @@ Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because Questionnaire_QR_Item_WrongType = Answer value must be of type {0} Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire -Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_NotFound = The questionnaire "{0}" could not be resolved, so no validation can be performed against the base questionnaire Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link -Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target "{0}" for this question enableWhen Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference 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_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_CantResolve = Unable to resolve resource '{0}' -Reference_REF_CantResolveProfile = Unable to resolve the profile reference '{0}' +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 ULR is allowed ([type]?parameters. Encountered {0}) Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} @@ -110,10 +110,10 @@ Reference_REF_NoDisplay = A Reference without an actual reference or identifier Reference_REF_NoType = Unable to determine type of target resource Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} -Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Reference_REF_WrongTarget = The type "{0}" is not a valid Target for this element (must be one of {1}) Resource_RES_ID_Missing = Resource requires an id, but none is present Resource_RES_ID_Prohibited = Resource has an id, but none is allowed -Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Terminology_PassThrough_TX_Message = {0} for "{1}#{2}" Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked Terminology_TX_Binding_Missing = Binding for {0} missing (cc) Terminology_TX_Binding_Missing2 = Binding for {0} missing @@ -132,7 +132,7 @@ Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Display_Wrong = Display should be "{0}" Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet Terminology_TX_Error_Coding1 = Error {0} validating Coding @@ -143,10 +143,10 @@ Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0 Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server -Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_15 = The value provided ("{0}") could not be validated in the absence of a terminology server +Terminology_TX_NoValid_16 = The value provided ("{0}") is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_17 = The value provided ("{0}") is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_18 = The value provided ("{0}") is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} @@ -156,37 +156,37 @@ Terminology_TX_NoValid_7 = None of the codes provided could be validated against Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) Terminology_TX_System_Invalid = Invalid System URI: {0} -Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_NotKnown = Code System URI "{0}" is unknown so the code cannot be validated Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference -Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_TX_System_Unknown = Unknown Code System "{0}" Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system -Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ("{0}") Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator -Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Base64_Valid = The value "{0}" is not a valid Base64 value Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' -Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error -Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_Code_WS = The code "{0}" is not valid (whitespace rules) +Type_Specific_Checks_DT_DateTime_Reasonable = The value "{0}" is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant "{0}" is not valid (by regex) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) -Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals -Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal -Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_Decimal_Range = The value "{0}" is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value "{0}" is not a valid decimal +Type_Specific_Checks_DT_ID_Valid = id value "{0}" is not valid Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) -Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer64_Valid = The value "{0}" is not a valid integer64 Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 -Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_Integer_Valid = The value "{0}" is not a valid integer Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: Type_Specific_Checks_DT_OID_Valid = OIDs must be valid Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty -Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_Regex = Element value "{0}" does not meet regex "{1}" Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) @@ -194,8 +194,8 @@ Type_Specific_Checks_DT_String_WS = value should not start or finish with whites Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: -Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') -Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace("{0}") +Type_Specific_Checks_DT_URL_Resolve = URL value "{0}" does not resolve Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader @@ -206,30 +206,30 @@ Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' +Validation_VAL_Profile_NoCheckMin = {0}": Unable to check minimum required ({1}) due to lack of slicing validation +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_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} Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} -Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order -Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked -Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element "{1}" is out of order +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element "{1}" is out of order in ordered slice +Validation_VAL_Profile_Unknown = Profile reference "{0}" could not be resolved, so has not been checked +Validation_VAL_Profile_WrongType = Specified profile type was "{0}", but found type "{1}" Validation_VAL_Unknown_Profile = Unknown profile {0} -XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') -XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') -XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') -XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div -_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ("{0}" on "{1}") +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ("{0}") +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ("{0}", should be "{1}") +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ("{0}") - must start with div +_DT_Fixed_Wrong = Value is "{0}" but must be "{1}" All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} -Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Unable_to_find_element_with_id_ = Unable to find element with id "{0}" Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} Unsupported_version_R1 = Unsupported version R1 @@ -256,7 +256,7 @@ Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profil Unable_to_resolve_profile_ = Unable to resolve profile {0} Resource_resolution_services_not_provided = Resource resolution services not provided Unrecognised_extension_context_ = Unrecognised extension context {0} -Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile "{0}" in order to validate against it Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element Not_supported_yet = Not supported yet diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index 001b8b057..11f2ce91b 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -1,23 +1,23 @@ #InstanceValidator Bundle_BUNDLE_Entry_Canonical = The canonical URL ({0}) cannot match the fullUrl ({1}) unless on the canonical server itself Bundle_BUNDLE_Entry_Document = The first entry in a document must be a composition -Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ('{0}' vs '{1}') +Bundle_BUNDLE_Entry_IdUrlMismatch = Resource ID does not match the ID in the entry full URL ("{0}" vs "{1}") Bundle_BUNDLE_Entry_MismatchIdUrl = The canonical URL ({0}) cannot match the fullUrl ({1}) unless the resource id ({2}) also matches Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one entry Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl -Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type '{0}' -Bundle_BUNDLE_Entry_NotFound = Can't find '{0}' in the bundle ({1}) +Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type "{0}" +Bundle_BUNDLE_Entry_NotFound = Can't find "{0}" in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_Type = The type '{0} is not valid - no resources allowed here -Bundle_BUNDLE_Entry_Type2 = The type '{0}' is not valid - must be {1} -Bundle_BUNDLE_Entry_Type3 = The type '{0}' is not valid - must be one of {1} +Bundle_BUNDLE_Entry_Type = The type "{0}" is not valid - no resources allowed here +Bundle_BUNDLE_Entry_Type2 = The type "{0}" is not valid - must be {1} +Bundle_BUNDLE_Entry_Type3 = The type "{0}" is not valid - must be one of {1} Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements -CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter '{0}' type is {1}, but type here is {2} +CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2} CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion @@ -28,19 +28,19 @@ Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extensio Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_EXT_Modifier_N = The Extension '{0}' must not be used as an extension (it's a modifierExtension) -Extension_EXT_Modifier_Y = The Extension '{0}' must be used as a modifierExtension -Extension_EXT_Simple = The Extension '{0}' definition is for a simple extension, so it must contain a value, not extensions -Extension_EXT_SubExtension_Invalid = Sub-extension url '{0}' is not defined by the Extension {1} -Extension_EXT_Type = The Extension '{0}' definition allows for the types {1} but found type {2} +Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_Y = The Extension "{0}" must be used as a modifierExtension +Extension_EXT_Simple = The Extension "{0}" definition is for a simple extension, so it must contain a value, not extensions +Extension_EXT_SubExtension_Invalid = Sub-extension url "{0}" is not defined by the Extension {1} +Extension_EXT_Type = The Extension "{0}" definition allows for the types {1} but found type {2} Extension_EXT_URL_Absolute = Extension.url must be an absolute URL Extension_EXT_Unknown = Unknown extension {0} Extension_EXT_Unknown_NotHere = The extension {0} is unknown, and not allowed here Extension_EXT_Url_NotFound = Extension.url is required -Extension_EXT_Version_Internal = Extension url '{0}' evaluation state illegal -Extension_EXT_Version_Invalid = Extension url '{0}' is not valid (invalid Version '{1}') -Extension_EXT_Version_InvalidId = Extension url '{0}' is not valid (invalid Element id '{1}') -Extension_EXT_Version_NoChange = Extension url '{0}' is not valid (Element id '{1}' is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) +Extension_EXT_Version_Internal = Extension url "{0}" evaluation state illegal +Extension_EXT_Version_Invalid = Extension url "{0}" is not valid (invalid Version "{1}") +Extension_EXT_Version_InvalidId = Extension url "{0}" is not valid (invalid Element id "{1}") +Extension_EXT_Version_NoChange = Extension url "{0}" is not valid (Element id "{1}" is valid, but cannot be used in a cross-version paradigm because there has been no changes across the relevant versions) Fixed_Type_Checks_DT_Address_Line = Expected {0} but found {1} line elements Fixed_Type_Checks_DT_Name_Family = Expected {0} but found {1} family elements Fixed_Type_Checks_DT_Name_Given = Expected {0} but found {1} given elements @@ -54,8 +54,8 @@ Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not h Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Meta_RES_Security_Duplicate = Duplicate Security Label {0} MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile -Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant '{1}') -Profile_VAL_MissingElement = Missing element '{0}' - required by fixed value assigned in profile {1} +Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant "{1}") +Profile_VAL_MissingElement = Missing element "{0}" - required by fixed value assigned in profile {1} Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile Questionnaire_QR_Item_BadOption = The value provided ({0}::{1}) is not in the options value set in the questionnaire Questionnaire_QR_Item_Coding = Error {0} validating Coding against Questionnaire Options @@ -79,8 +79,8 @@ Questionnaire_QR_Item_NoString = The string {0} is not a valid option Questionnaire_QR_Item_NoTime = The time {0} is not a valid option Questionnaire_QR_Item_NoType = Definition for item {0} does not contain a type Questionnaire_QR_Item_NotEnabled = Item has answer (2), even though it is not enabled {0} -Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = '{0}') -Questionnaire_QR_Item_NotFound = LinkId '{0}' not found in questionnaire +Questionnaire_QR_Item_NotEnabled2 = Item has answer, even though it is not enabled (item id = "{0}") +Questionnaire_QR_Item_NotFound = LinkId "{0}" not found in questionnaire Questionnaire_QR_Item_OnlyOneA = Only one response answer item with this linkId allowed Questionnaire_QR_Item_OnlyOneI = Only one response item with this linkId allowed - {0} Questionnaire_QR_Item_Order = Structural Error: items are out of order @@ -90,19 +90,19 @@ Questionnaire_QR_Item_TimeNoOptions = Cannot validate time answer option because Questionnaire_QR_Item_WrongType = Answer value must be of type {0} Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire -Questionnaire_QR_Q_NotFound = The questionnaire '{0}' could not be resolved, so no validation can be performed against the base questionnaire +Questionnaire_QR_Q_NotFound = The questionnaire "{0}" could not be resolved, so no validation can be performed against the base questionnaire Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link -Questionnaire_Q_EnableWhen_NoTarget = Unable to find target '{0}' for this question enableWhen +Questionnaire_Q_EnableWhen_NoTarget = Unable to find target "{0}" for this question enableWhen Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference 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_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_CantResolve = Unable to resolve resource '{0}' -Reference_REF_CantResolveProfile = Unable to resolve the profile reference '{0}' +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 ULR is allowed ([type]?parameters. Encountered {0}) Reference_REF_Format2 = Relative URLs must be of the format [ResourceName]/[id]. Encountered {0} Reference_REF_MultipleMatches = Found multiple matching profiles for {0} among choices: {1} @@ -110,10 +110,10 @@ Reference_REF_NoDisplay = A Reference without an actual reference or identifier Reference_REF_NoType = Unable to determine type of target resource Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} -Reference_REF_WrongTarget = The type '{0}' is not a valid Target for this element (must be one of {1}) +Reference_REF_WrongTarget = The type "{0}" is not a valid Target for this element (must be one of {1}) Resource_RES_ID_Missing = Resource requires an id, but none is present Resource_RES_ID_Prohibited = Resource has an id, but none is allowed -Terminology_PassThrough_TX_Message = {0} for '{1}#{2}' +Terminology_PassThrough_TX_Message = {0} for "{1}#{2}" Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked Terminology_TX_Binding_Missing = Binding for {0} missing (cc) Terminology_TX_Binding_Missing2 = Binding for {0} missing @@ -132,7 +132,7 @@ Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the Terminology_TX_Confirm_4 = Could not confirm that the codes provided are in the value set {0}, and a code from this value set is required Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set -Terminology_TX_Display_Wrong = Display should be '{0}' +Terminology_TX_Display_Wrong = Display should be "{0}" Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet Terminology_TX_Error_Coding1 = Error {0} validating Coding @@ -143,10 +143,10 @@ Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0 Terminology_TX_NoValid_12 = The Coding provided is not in the value set {0}, and a code is required from this value set. {1} Terminology_TX_NoValid_13 = The Coding provided is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} Terminology_TX_NoValid_14 = The Coding provided is not in the value set {0}, and a code is recommended to come from this value set. {1} -Terminology_TX_NoValid_15 = The value provided ('{0}') could not be validated in the absence of a terminology server -Terminology_TX_NoValid_16 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is required from this value set){3} -Terminology_TX_NoValid_17 = The value provided ('{0}') is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} -Terminology_TX_NoValid_18 = The value provided ('{0}') is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} +Terminology_TX_NoValid_15 = The value provided ("{0}") could not be validated in the absence of a terminology server +Terminology_TX_NoValid_16 = The value provided ("{0}") is not in the value set {1} ({2}, and a code is required from this value set){3} +Terminology_TX_NoValid_17 = The value provided ("{0}") is not in the value set {1} ({2}, and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_18 = The value provided ("{0}") is not in the value set {1} ({2}, and a code is recommended to come from this value set){3} Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}, and a code should come from this value set unless it has no suitable code) (codes = {2}) Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}, and a code is recommended to come from this value set) (codes = {2}) Terminology_TX_NoValid_4 = The Coding provided is not in the value set {0}, and a code is required from this value set{1} @@ -156,37 +156,37 @@ Terminology_TX_NoValid_7 = None of the codes provided could be validated against Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}, and a code from this value set is required) (codes = {2}) Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) Terminology_TX_System_Invalid = Invalid System URI: {0} -Terminology_TX_System_NotKnown = Code System URI '{0}' is unknown so the code cannot be validated +Terminology_TX_System_NotKnown = Code System URI "{0}" is unknown so the code cannot be validated Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference -Terminology_TX_System_Unknown = Unknown Code System '{0}' +Terminology_TX_System_Unknown = Unknown Code System "{0}" Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system -Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ('{0}') +Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system ("{0}") Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator -Type_Specific_Checks_DT_Base64_Valid = The value '{0}' is not a valid Base64 value +Type_Specific_Checks_DT_Base64_Valid = The value "{0}" is not a valid Base64 value Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' -Type_Specific_Checks_DT_Code_WS = The code '{0}' is not valid (whitespace rules) -Type_Specific_Checks_DT_DateTime_Reasonable = The value '{0}' is outside the range of reasonable years - check for data entry error -Type_Specific_Checks_DT_DateTime_Regex = The instant '{0}' is not valid (by regex) +Type_Specific_Checks_DT_Code_WS = The code "{0}" is not valid (whitespace rules) +Type_Specific_Checks_DT_DateTime_Reasonable = The value "{0}" is outside the range of reasonable years - check for data entry error +Type_Specific_Checks_DT_DateTime_Regex = The instant "{0}" is not valid (by regex) Type_Specific_Checks_DT_DateTime_TZ = if a date has a time, it must have a timezone Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0}) Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0}) -Type_Specific_Checks_DT_Decimal_Range = The value '{0}' is outside the range of commonly/reasonably supported decimals -Type_Specific_Checks_DT_Decimal_Valid = The value '{0}' is not a valid decimal -Type_Specific_Checks_DT_ID_Valid = id value '{0}' is not valid +Type_Specific_Checks_DT_Decimal_Range = The value "{0}" is outside the range of commonly/reasonably supported decimals +Type_Specific_Checks_DT_Decimal_Valid = The value "{0}" is not a valid decimal +Type_Specific_Checks_DT_ID_Valid = id value "{0}" is not valid Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0}) -Type_Specific_Checks_DT_Integer64_Valid = The value '{0}' is not a valid integer64 +Type_Specific_Checks_DT_Integer64_Valid = The value "{0}" is not a valid integer64 Type_Specific_Checks_DT_Integer_GT = value is greater than permitted maximum value of {0} Type_Specific_Checks_DT_Integer_LT = value is less than permitted minimum value of {0} Type_Specific_Checks_DT_Integer_LT0 = value is less than permitted minimum value of 0 Type_Specific_Checks_DT_Integer_LT1 = value is less than permitted minimum value of 1 -Type_Specific_Checks_DT_Integer_Valid = The value '{0}' is not a valid integer +Type_Specific_Checks_DT_Integer_Valid = The value "{0}" is not a valid integer Type_Specific_Checks_DT_OID_Start = OIDs must start with urn:oid: Type_Specific_Checks_DT_OID_Valid = OIDs must be valid Type_Specific_Checks_DT_Primitive_Length = value is longer than permitted maximum length of {0} Type_Specific_Checks_DT_Primitive_NotEmpty = @value cannot be empty -Type_Specific_Checks_DT_Primitive_Regex = Element value '{0}' does not meet regex '{1}' +Type_Specific_Checks_DT_Primitive_Regex = Element value "{0}" does not meet regex "{1}" Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value or must have child extensions Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) @@ -194,8 +194,8 @@ Type_Specific_Checks_DT_String_WS = value should not start or finish with whites Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: -Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace('{0}') -Type_Specific_Checks_DT_URL_Resolve = URL value '{0}' does not resolve +Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace("{0}") +Type_Specific_Checks_DT_URL_Resolve = URL value "{0}" does not resolve Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid: Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0}) Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader @@ -206,30 +206,30 @@ Validation_VAL_Profile_Maximum = {0}: max allowed = {1}, but found {2} Validation_VAL_Profile_Minimum = {0}: minimum required = {1}, but only found {2} Validation_VAL_Profile_MultipleMatches = Found multiple matching profiles among choices: {0} Validation_VAL_Profile_NoCheckMax = {0}: Unable to check max allowed ({1}) due to lack of slicing validation -Validation_VAL_Profile_NoCheckMin = {0}': Unable to check minimum required ({1}) due to lack of slicing validation -Validation_VAL_Profile_NoDefinition = No definition found for resource type '{0}' +Validation_VAL_Profile_NoCheckMin = {0}": Unable to check minimum required ({1}) due to lack of slicing validation +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_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} Validation_VAL_Profile_NotSlice = This element does not match any known slice {0} -Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element '{1}' is out of order -Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element '{1}' is out of order in ordered slice -Validation_VAL_Profile_Unknown = Profile reference '{0}' could not be resolved, so has not been checked -Validation_VAL_Profile_WrongType = Specified profile type was '{0}', but found type '{1}' +Validation_VAL_Profile_OutOfOrder = As specified by profile {0}, Element "{1}" is out of order +Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element "{1}" is out of order in ordered slice +Validation_VAL_Profile_Unknown = Profile reference "{0}" could not be resolved, so has not been checked +Validation_VAL_Profile_WrongType = Specified profile type was "{0}", but found type "{1}" Validation_VAL_Unknown_Profile = Unknown profile {0} -XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ('{0}' on '{1}') -XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ('{0}') -XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ('{0}', should be '{1}') -XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ('{0}') - must start with div -_DT_Fixed_Wrong = Value is '{0}' but must be '{1}' +XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML ("{0}" on "{1}") +XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML ("{0}") +XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML ("{0}", should be "{1}") +XHTML_XHTML_Name_Invalid = Wrong name on the XHTML ("{0}") - must start with div +_DT_Fixed_Wrong = Value is "{0}" but must be "{1}" All_observations_should_have_an_effectiveDateTime_or_an_effectivePeriod = All observations should have an effectiveDateTime or an effectivePeriod All_observations_should_have_a_performer = All observations should have a performer All_observations_should_have_a_subject = All observations should have a subject Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} -Unable_to_find_element_with_id_ = Unable to find element with id '{0}' +Unable_to_find_element_with_id_ = Unable to find element with id "{0}" Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} Unsupported_version_R1 = Unsupported version R1 @@ -256,7 +256,7 @@ Unable_to_resolve_element__in_profile_ = Unable to resolve element {0} in profil Unable_to_resolve_profile_ = Unable to resolve profile {0} Resource_resolution_services_not_provided = Resource resolution services not provided Unrecognised_extension_context_ = Unrecognised extension context {0} -Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile '{0}' in order to validate against it +Unable_to_locate_the_profile__in_order_to_validate_against_it = Unable to locate the profile "{0}" in order to validate against it Reference__refers_to_a__not_a_ValueSet = Reference {0} refers to a {1} not a ValueSet Not_done_yet_ValidatorHostServicesconformsToProfile_when_item_is_not_an_element = Not done yet (ValidatorHostServices.conformsToProfile), when item is not an element Not_supported_yet = Not supported yet From 08447d040e03422ab41c619936fab28af1cba07f Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Mon, 2 Mar 2020 00:07:52 +0100 Subject: [PATCH 30/52] escaped needed single quotes --- .../src/main/resources/Messages.properties | 32 +++++++++---------- .../main/resources/Messages_de_DE.properties | 32 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/resources/Messages.properties b/org.hl7.fhir.validation/src/main/resources/Messages.properties index 11f2ce91b..5a4646466 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages.properties @@ -7,8 +7,8 @@ Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one en Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type "{0}" -Bundle_BUNDLE_Entry_NotFound = Can't find "{0}" in the bundle ({1}) -Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry +Bundle_BUNDLE_Entry_NotFound = Can''t find "{0}" in the bundle ({1}) +Bundle_BUNDLE_Entry_Orphan = Entry {0} isn''t reachable by traversing from first Bundle entry Bundle_BUNDLE_Entry_Type = The type "{0}" is not valid - no resources allowed here Bundle_BUNDLE_Entry_Type2 = The type "{0}" is not valid - must be {1} Bundle_BUNDLE_Entry_Type3 = The type "{0}" is not valid - must be one of {1} @@ -18,17 +18,17 @@ Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2} -CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a ''all system'' value set of {1}, but the include has extra details +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a ''all system'' value set of {1}, but it is an expansion +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a matching system ({2}) Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} -Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn''t contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it''s a modifierExtension) Extension_EXT_Modifier_Y = The Extension "{0}" must be used as a modifierExtension Extension_EXT_Simple = The Extension "{0}" definition is for a simple extension, so it must contain a value, not extensions Extension_EXT_SubExtension_Invalid = Sub-extension url "{0}" is not defined by the Extension {1} @@ -53,7 +53,7 @@ Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not h Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Meta_RES_Security_Duplicate = Duplicate Security Label {0} -MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +MustSupport_VAL_MustSupport = The element {0} is not marked as ''mustSupport'' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant "{1}") Profile_VAL_MissingElement = Missing element "{0}" - required by fixed value assigned in profile {1} Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile @@ -68,7 +68,7 @@ Questionnaire_QR_Item_Missing = No response answer found for required item {0} Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option Questionnaire_QR_Item_NoDate = The date {0} is not a valid option Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option -Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_NoLinkId = No LinkId, so can''t be validated Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date @@ -92,11 +92,11 @@ Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire Questionnaire_QR_Q_NotFound = The questionnaire "{0}" could not be resolved, so no validation can be performed against the base questionnaire Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself -Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it''s enableWhen condition Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link Questionnaire_Q_EnableWhen_NoTarget = Unable to find target "{0}" for this question enableWhen -Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself -Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can''t reference itself +Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference 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} @@ -118,8 +118,8 @@ Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked Terminology_TX_Binding_Missing = Binding for {0} missing (cc) Terminology_TX_Binding_Missing2 = Binding for {0} missing Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server -Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked -Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can''t be checked +Terminology_TX_Binding_NoSource2 = Binding has no source, so can''t be checked Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) @@ -164,7 +164,7 @@ Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator Type_Specific_Checks_DT_Base64_Valid = The value "{0}" is not a valid Base64 value -Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Boolean_Value = boolean values must be ''true'' or ''false'' Type_Specific_Checks_DT_Code_WS = The code "{0}" is not valid (whitespace rules) Type_Specific_Checks_DT_DateTime_Reasonable = The value "{0}" is outside the range of reasonable years - check for data entry error Type_Specific_Checks_DT_DateTime_Regex = The instant "{0}" is not valid (by regex) diff --git a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties index 11f2ce91b..5a4646466 100644 --- a/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties +++ b/org.hl7.fhir.validation/src/main/resources/Messages_de_DE.properties @@ -7,8 +7,8 @@ Bundle_BUNDLE_Entry_NoFirst = Documents or Messages must contain at least one en Bundle_BUNDLE_Entry_NoFirstResource = No resource on first entry Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type "{0}" -Bundle_BUNDLE_Entry_NotFound = Can't find "{0}" in the bundle ({1}) -Bundle_BUNDLE_Entry_Orphan = Entry {0} isn't reachable by traversing from first Bundle entry +Bundle_BUNDLE_Entry_NotFound = Can''t find "{0}" in the bundle ({1}) +Bundle_BUNDLE_Entry_Orphan = Entry {0} isn''t reachable by traversing from first Bundle entry Bundle_BUNDLE_Entry_Type = The type "{0}" is not valid - no resources allowed here Bundle_BUNDLE_Entry_Type2 = The type "{0}" is not valid - must be {1} Bundle_BUNDLE_Entry_Type3 = The type "{0}" is not valid - must be one of {1} @@ -18,17 +18,17 @@ Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0} Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0} Bundle_MSG_Event_Count = Expected {0} but found {1} event elements CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2} -CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a 'all system' value set of {1}, but the include has extra details -CodeSystem_CS_VS_Invalid = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a single include -CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a 'all system' value set of {1}, but it is an expansion -CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a 'all system' value set of {1}, but doesn't have a matching system ({2}) +CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a ''all system'' value set of {1}, but the include has extra details +CodeSystem_CS_VS_Invalid = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a single include +CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a ''all system'' value set of {1}, but it is an expansion +CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a matching system ({2}) Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} -Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn't contain any extensions +Extension_EXT_Fixed_Banned = No extensions allowed, as the specified fixed value doesn''t contain any extensions Extension_EXT_Modifier_MismatchN = Extension modifier mismatch: the extension element is not labelled as a modifier, but the underlying extension is Extension_EXT_Modifier_MismatchY = Extension modifier mismatch: the extension element is labelled as a modifier, but the underlying extension is not -Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it's a modifierExtension) +Extension_EXT_Modifier_N = The Extension "{0}" must not be used as an extension (it''s a modifierExtension) Extension_EXT_Modifier_Y = The Extension "{0}" must be used as a modifierExtension Extension_EXT_Simple = The Extension "{0}" definition is for a simple extension, so it must contain a value, not extensions Extension_EXT_SubExtension_Invalid = Sub-extension url "{0}" is not defined by the Extension {1} @@ -53,7 +53,7 @@ Language_XHTML_Lang_Missing1 = Resource has a language, but the XHTML does not h Language_XHTML_Lang_Missing2 = Resource has a language, but the XHTML does not have a lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Language_XHTML_Lang_Missing3 = Resource has a language, but the XHTML does not have an xml:lang tag (needs both lang and xml:lang - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues) Meta_RES_Security_Duplicate = Duplicate Security Label {0} -MustSupport_VAL_MustSupport = The element {0} is not marked as 'mustSupport' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile +MustSupport_VAL_MustSupport = The element {0} is not marked as ''mustSupport'' in the profile {1}. Consider not using the element, or marking the element as must-Support in the profile Profile_EXT_Not_Here = The extension {0} is not allowed to be used at this point (based on context invariant "{1}") Profile_VAL_MissingElement = Missing element "{0}" - required by fixed value assigned in profile {1} Profile_VAL_NotAllowed = The element {0} is present in the instance but not allowed in the applicable {1} specified in profile @@ -68,7 +68,7 @@ Questionnaire_QR_Item_Missing = No response answer found for required item {0} Questionnaire_QR_Item_NoCoding = The code {0}::{1} is not a valid option Questionnaire_QR_Item_NoDate = The date {0} is not a valid option Questionnaire_QR_Item_NoInteger = The integer {0} is not a valid option -Questionnaire_QR_Item_NoLinkId = No LinkId, so can't be validated +Questionnaire_QR_Item_NoLinkId = No LinkId, so can''t be validated Questionnaire_QR_Item_NoOptions = Cannot validate options because no option or options are provided Questionnaire_QR_Item_NoOptionsCoding = Option list has no option values of type coding Questionnaire_QR_Item_NoOptionsDate = Option list has no option values of type date @@ -92,11 +92,11 @@ Questionnaire_QR_Item_WrongType2 = Answer value must be one of the types {0} Questionnaire_QR_Q_None = No questionnaire is identified, so no validation can be performed against the base questionnaire Questionnaire_QR_Q_NotFound = The questionnaire "{0}" could not be resolved, so no validation can be performed against the base questionnaire Questionnaire_Q_EnableWhen_After = The target of this enableWhen rule ({0}) comes after the question itself -Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it's enableWhen condition +Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer to an inner question for it''s enableWhen condition Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link Questionnaire_Q_EnableWhen_NoTarget = Unable to find target "{0}" for this question enableWhen -Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can't reference itself -Reference_REF_Aggregation = Reference is {0} which isn't supported by the specified aggregation mode(s) for the reference +Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can''t reference itself +Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference 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} @@ -118,8 +118,8 @@ Terminology_TX_Binding_CantCheck = Binding by URI reference cannot be checked Terminology_TX_Binding_Missing = Binding for {0} missing (cc) Terminology_TX_Binding_Missing2 = Binding for {0} missing Terminology_TX_Binding_NoServer = The value provided could not be validated in the absence of a terminology server -Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can't be checked -Terminology_TX_Binding_NoSource2 = Binding has no source, so can't be checked +Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can''t be checked +Terminology_TX_Binding_NoSource2 = Binding has no source, so can''t be checked Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) @@ -164,7 +164,7 @@ Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator Type_Specific_Checks_DT_Base64_Valid = The value "{0}" is not a valid Base64 value -Type_Specific_Checks_DT_Boolean_Value = boolean values must be 'true' or 'false' +Type_Specific_Checks_DT_Boolean_Value = boolean values must be ''true'' or ''false'' Type_Specific_Checks_DT_Code_WS = The code "{0}" is not valid (whitespace rules) Type_Specific_Checks_DT_DateTime_Reasonable = The value "{0}" is outside the range of reasonable years - check for data entry error Type_Specific_Checks_DT_DateTime_Regex = The instant "{0}" is not valid (by regex) From 9178945241ecad08918fef38a15b89710e692da6 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 1 Mar 2020 21:38:36 -0500 Subject: [PATCH 31/52] Correct copy-paste error in ValidationOptions --- .../org/hl7/fhir/utilities/validation/ValidationOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java index 8924c030f..ad4325d11 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/validation/ValidationOptions.java @@ -67,7 +67,7 @@ public class ValidationOptions { public ValidationOptions guessSystem() { ValidationOptions n = this.copy(); - n.guessSystem = false; + n.guessSystem = true; return n; } From 109dea9a02bde2a1a22edb8a1ce4071d9ba7d197 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Mon, 2 Mar 2020 06:18:00 -0500 Subject: [PATCH 32/52] Use ValidationOptions instead of TerminologyServiceOptions --- .../fhir/r4/context/BaseWorkerContext.java | 28 +++++++++++-------- .../hl7/fhir/r4/context/IWorkerContext.java | 13 +++++---- .../hl7/fhir/r4/context/TerminologyCache.java | 6 ++-- .../terminologies/ValueSetCheckerSimple.java | 6 ++-- .../fhir/r5/context/BaseWorkerContext.java | 1 - .../hl7/fhir/r5/context/IWorkerContext.java | 1 - .../terminologies/ValueSetCheckerSimple.java | 1 - .../hl7/fhir/validation/BaseValidator.java | 3 +- .../instance/InstanceValidator.java | 3 +- 9 files changed, 33 insertions(+), 29 deletions(-) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java index 2b57812aa..75a4b365a 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java @@ -45,17 +45,21 @@ import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.utilities.OIDUtils; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; +import org.hl7.fhir.utilities.validation.ValidationOptions; import java.io.FileNotFoundException; import java.io.IOException; -import java.util.*; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; public abstract class BaseWorkerContext implements IWorkerContext { @@ -438,25 +442,25 @@ public abstract class BaseWorkerContext implements IWorkerContext { // --- validate code ------------------------------------------------------------------------------- @Override - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display) { + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display) { Coding c = new Coding(system, code, display); return validateCode(options, c, null); } @Override - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs) { + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display, ValueSet vs) { Coding c = new Coding(system, code, display); return validateCode(options, c, vs); } @Override - public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs) { + public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs) { Coding c = new Coding(null, code, null); return doValidateCode(options, c, vs, true); } @Override - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi) { + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display, ConceptSetComponent vsi) { Coding c = new Coding(system, code, display); ValueSet vs = new ValueSet(); vs.setUrl(Utilities.makeUuidUrn()); @@ -465,11 +469,11 @@ public abstract class BaseWorkerContext implements IWorkerContext { } @Override - public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs) { + public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) { return doValidateCode(options, code, vs, false); } - public ValidationResult doValidateCode(TerminologyServiceOptions options, Coding code, ValueSet vs, boolean implySystem) { + public ValidationResult doValidateCode(ValidationOptions options, Coding code, ValueSet vs, boolean implySystem) { CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null; ValidationResult res = null; if (txCache != null) @@ -511,7 +515,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { return res; } - private void setTerminologyOptions(TerminologyServiceOptions options, Parameters pIn) { + private void setTerminologyOptions(ValidationOptions options, Parameters pIn) { if (options != null) { if (!Utilities.noString(options.getLanguage())) pIn.addParameter("displayLanguage", options.getLanguage()); @@ -519,7 +523,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { } @Override - public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) { + public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs) { CacheToken cacheToken = txCache.generateValidationToken(options, code, vs); ValidationResult res = txCache.getValidation(cacheToken); if (res != null) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java index 352b22927..765379522 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java @@ -51,6 +51,7 @@ import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; +import org.hl7.fhir.utilities.validation.ValidationOptions; /** @@ -376,7 +377,7 @@ public interface IWorkerContext { * @param display * @return */ - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display); + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display); /** * Validation of a code - consult the terminology service @@ -392,10 +393,10 @@ public interface IWorkerContext { * @param display * @return */ - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs); - public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs); - public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs); - public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs); + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display, ValueSet vs); + public ValidationResult validateCode(ValidationOptions options, String code, ValueSet vs); + public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs); + public ValidationResult validateCode(ValidationOptions options, CodeableConcept code, ValueSet vs); /** * Validation of a code - consult the terminology service @@ -411,7 +412,7 @@ public interface IWorkerContext { * @param display * @return */ - public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi); + public ValidationResult validateCode(ValidationOptions options, String system, String code, String display, ConceptSetComponent vsi); /** * returns the recommended tla for the type diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java index b5f7df446..3939dc4f5 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java @@ -46,7 +46,6 @@ import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -55,6 +54,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import org.hl7.fhir.utilities.validation.ValidationOptions; /** * This implements a two level cache. @@ -112,7 +112,7 @@ public class TerminologyCache { load(); } - public CacheToken generateValidationToken(TerminologyServiceOptions options, Coding code, ValueSet vs) { + public CacheToken generateValidationToken(ValidationOptions options, Coding code, ValueSet vs) { CacheToken ct = new CacheToken(); if (code.hasSystem()) ct.name = getNameForSystem(code.getSystem()); @@ -130,7 +130,7 @@ public class TerminologyCache { return ct; } - public CacheToken generateValidationToken(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) { + public CacheToken generateValidationToken(ValidationOptions options, CodeableConcept code, ValueSet vs) { CacheToken ct = new CacheToken(); for (Coding c : code.getCoding()) { if (c.hasSystem()) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java index 084d4dd40..ceb1b70e6 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java @@ -30,8 +30,8 @@ import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionDesignationComponent; import org.hl7.fhir.r4.model.ValueSet.*; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; +import org.hl7.fhir.utilities.validation.ValidationOptions; import java.util.ArrayList; import java.util.HashMap; @@ -43,9 +43,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker { private ValueSet valueset; private IWorkerContext context; private Map inner = new HashMap<>(); - private TerminologyServiceOptions options; + private ValidationOptions options; - public ValueSetCheckerSimple(TerminologyServiceOptions options, ValueSet source, IWorkerContext context) { + public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context) { this.valueset = source; this.context = context; this.options = options; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 6f6437b41..526e2f431 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -83,7 +83,6 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.client.ToolingClientLogger; import org.hl7.fhir.utilities.OIDUtils; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 242055e44..0b500b26e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -51,7 +51,6 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorCla import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.INarrativeGenerator; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 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 bca063a1d..2cefc64ce 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 @@ -45,7 +45,6 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index c95e1f364..1a0b6e97a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -171,7 +171,8 @@ public class BaseValidator { */ protected boolean hint(List errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { if (!thePass) { - addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION); + String message = formatMessage(msg); + addValidationMessage(errors, type, line, col, path, message, IssueSeverity.INFORMATION); } return thePass; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index b0343a234..d581ec4de 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -2016,7 +2016,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); ValidationResult vr = null; if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = context.validateCode(new ValidationOptions(stack.workingLang), value, vs); + ValidationOptions options = new ValidationOptions(stack.workingLang).guessSystem(); + vr = context.validateCode(options, value, vs); } txTime = txTime + (System.nanoTime() - t); if (vr != null && !vr.isOk()) { From 3684fbb8c91fb147cb3362ee3c88c9a1f476a0b5 Mon Sep 17 00:00:00 2001 From: Oliver Egger Date: Mon, 2 Mar 2020 18:38:02 +0100 Subject: [PATCH 33/52] adjust atc who url --- .../src/main/java/org/hl7/fhir/utilities/OIDUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java index 347208053..03b0c86df 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/OIDUtils.java @@ -49,7 +49,7 @@ public class OIDUtils { if (r.equals("2.16.840.1.113883.6.103")) return "http://hl7.org/fhir/sid/icd-9"; //todo: confirm this if (r.equals("2.16.840.1.113883.6.73")) - return "http://hl7.org/fhir/sid/atc"; + return "http://www.whocc.no/atc"; if (r.equals("2.16.840.1.113883.3.26.1.1")) return "http://ncimeta.nci.nih.gov"; if (r.equals("2.16.840.1.113883.3.26.1.1.1")) From d6018f26c82683e7d7f97bbe95bbc9165760d118 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 07:38:52 +1100 Subject: [PATCH 34/52] fix minor bugs loading packages --- .../java/org/hl7/fhir/utilities/cache/NpmPackage.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index e2abec238..2ab1a3389 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -200,7 +200,7 @@ public class NpmPackage { File dir = new File(path); for (File f : dir.listFiles()) { - if (!Utilities.existsInList(f.getName(), ".git", ".svn") && !Utilities.existsInList(f.getName(), exemptions)) { + if (!isInternalExemptFile(f) && !Utilities.existsInList(f.getName(), exemptions)) { if (f.isDirectory()) { String d = f.getName(); if (!d.equals("package")) { @@ -227,6 +227,10 @@ public class NpmPackage { } } + public static boolean isInternalExemptFile(File f) { + return Utilities.existsInList(f.getName(), ".git", ".svn") || Utilities.existsInList(f.getName(), "package-list.json"); + } + private static void loadSubFolders(NpmPackage res, String rootPath, File dir) throws IOException { for (File f : dir.listFiles()) { if (f.isDirectory()) { @@ -684,7 +688,7 @@ public class NpmPackage { for (NpmPackageFolder folder : folders.values()) { String n = folder.name; - if (!"package".equals(n)) { + if (!"package".equals(n) && !(n.startsWith("package/") || n.startsWith("package\\"))) { n = "package/"+n; } NpmPackageIndexBuilder indexer = new NpmPackageIndexBuilder(); @@ -828,7 +832,7 @@ public class NpmPackage { NpmPackageFolder pf = folders.get(folder); String p = folder.contains("$") ? path : Utilities.path(path, folder); for (File f : new File(p).listFiles()) { - if (!f.isDirectory()) { + if (!f.isDirectory() && !isInternalExemptFile(f)) { pf.getContent().put(f.getName(), TextFile.fileToBytes(f)); } } From 4bc81c0e0c87878e3f6f67780dea76cf34347253 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 07:39:41 +1100 Subject: [PATCH 35/52] Handle missing terminology server better --- .../java/org/hl7/fhir/r5/context/BaseWorkerContext.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 6f6437b41..14e7deb92 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -669,7 +669,12 @@ public abstract class BaseWorkerContext implements IWorkerContext { if (expParameters == null) throw new Error("No ExpansionProfile provided"); pin.addParameter().setName("profile").setResource(expParameters); - txLog.clearLastId(); + if (txLog != null) { + txLog.clearLastId(); + } + if (txClient == null) { + throw new FHIRException("Attempt to use Terminology server when no Terminology server is available"); + } Parameters pOut; if (vs == null) pOut = txClient.validateCS(pin); From f1d81dde81f26f409765cbbec70e327a16cde7b3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 07:40:37 +1100 Subject: [PATCH 36/52] fix version extension package issue --- .../src/main/java/org/hl7/fhir/validation/ValidationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 69b434d3a..ecf94ccc9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -342,7 +342,7 @@ public class ValidationEngine implements IValidatorResourceFetcher { context = SimpleWorkerContext.fromDefinitions(source, loaderForVersion()); context.setAllowLoadingDuplicates(true); // because of Forge context.setExpansionProfile(makeExpProfile()); - NpmPackage npm = pcm.loadPackage("hl7.fhir.xver-extensions", "0.0.1"); + NpmPackage npm = pcm.loadPackage("hl7.fhir.xver-extensions", "0.0.2"); context.loadFromPackage(npm, null); grabNatives(source, "http://hl7.org/fhir"); } From 09e3816d37d29ecb9dea4cc174bb078eed3d6f0e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 07:41:34 +1100 Subject: [PATCH 37/52] make ProfileUtilities methods not static --- .../hl7/fhir/r5/conformance/ProfileUtilities.java | 12 ++++++------ .../org/hl7/fhir/r5/conformance/ShExGenerator.java | 8 +++++--- .../hl7/fhir/r5/conformance/XmlSchemaGenerator.java | 6 ++++-- .../hl7/fhir/r5/elementmodel/ObjectConverter.java | 4 +++- .../java/org/hl7/fhir/r5/elementmodel/Property.java | 12 +++++++----- .../java/org/hl7/fhir/r5/utils/FHIRPathEngine.java | 12 +++++++----- .../hl7/fhir/r5/utils/GraphQLSchemaGenerator.java | 4 +++- .../org/hl7/fhir/r5/utils/NarrativeGenerator.java | 10 ++++++---- .../org/hl7/fhir/r5/utils/QuestionnaireBuilder.java | 4 +++- .../org/hl7/fhir/r5/utils/StructureMapUtilities.java | 9 +++++++-- .../fhir/validation/instance/InstanceValidator.java | 8 +++++--- 11 files changed, 56 insertions(+), 33 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 80a7723ef..a9135390a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -315,7 +315,7 @@ public class ProfileUtilities extends TranslatingUtilities { - public static List getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { + public List getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { if (element.getContentReference()!=null) { for (ElementDefinition e : profile.getSnapshot().getElement()) { if (element.getContentReference().equals("#"+e.getId())) @@ -341,7 +341,7 @@ public class ProfileUtilities extends TranslatingUtilities { } - public static List getSliceList(StructureDefinition profile, ElementDefinition element) throws DefinitionException { + public List getSliceList(StructureDefinition profile, ElementDefinition element) throws DefinitionException { if (!element.hasSlicing()) throw new Error("getSliceList should only be called when the element has slicing"); @@ -368,11 +368,11 @@ public class ProfileUtilities extends TranslatingUtilities { * @param path The path of the element within the structure to get the children for * @return A List containing the element children (all of them are Elements) */ - public static List getChildList(StructureDefinition profile, String path, String id) { + public List getChildList(StructureDefinition profile, String path, String id) { return getChildList(profile, path, id, false); } - public static List getChildList(StructureDefinition profile, String path, String id, boolean diff) { + public List getChildList(StructureDefinition profile, String path, String id, boolean diff) { List res = new ArrayList(); boolean capturing = id==null; @@ -415,11 +415,11 @@ public class ProfileUtilities extends TranslatingUtilities { return res; } - public static List getChildList(StructureDefinition structure, ElementDefinition element, boolean diff) { + public List getChildList(StructureDefinition structure, ElementDefinition element, boolean diff) { return getChildList(structure, element.getPath(), element.getId(), diff); } - public static List getChildList(StructureDefinition structure, ElementDefinition element) { + public List getChildList(StructureDefinition structure, ElementDefinition element) { return getChildList(structure, element.getPath(), element.getId(), false); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java index 89e00365c..9a8b78b17 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java @@ -188,6 +188,7 @@ public class ShExGenerator { * this makes internal metadata services available to the generator - retrieving structure definitions, and value set expansion etc */ private IWorkerContext context; + private ProfileUtilities profileUtilities; /** * innerTypes -- inner complex types. Currently flattened in ShEx (doesn't have to be, btw) @@ -208,6 +209,7 @@ public class ShExGenerator { public ShExGenerator(IWorkerContext context) { super(); this.context = context; + profileUtilities = new ProfileUtilities(context, null, null); innerTypes = new HashSet>(); emittedInnerTypes = new HashSet>(); datatypes = new HashSet(); @@ -486,7 +488,7 @@ public class ShExGenerator { element_def.add("id", "fhir:" + (id.charAt(0) == id.toLowerCase().charAt(0)? shortId : id) + " "); } - List children = ProfileUtilities.getChildList(sd, ed); + List children = profileUtilities.getChildList(sd, ed); if (children.size() > 0) { innerTypes.add(new ImmutablePair(sd, ed)); defn = simpleElement(sd, ed, id); @@ -571,7 +573,7 @@ public class ShExGenerator { if(typ.hasProfile()) { if(typ.getWorkingCode().equals("Reference")) return genReference("", typ); - else if(ProfileUtilities.getChildList(sd, ed).size() > 0) { + else if(profileUtilities.getChildList(sd, ed).size() > 0) { // inline anonymous type - give it a name and factor it out innerTypes.add(new ImmutablePair(sd, ed)); return simpleElement(sd, ed, id); @@ -704,7 +706,7 @@ public class ShExGenerator { element_reference.add("comment", comment == null? " " : "# " + comment); List elements = new ArrayList(); - for (ElementDefinition child: ProfileUtilities.getChildList(sd, path, null)) + for (ElementDefinition child: profileUtilities.getChildList(sd, path, null)) elements.add(genElementDefinition(sd, child)); element_reference.add("elements", StringUtils.join(elements, "\n")); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/XmlSchemaGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/XmlSchemaGenerator.java index 6fa652aa8..dde500b8d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/XmlSchemaGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/XmlSchemaGenerator.java @@ -107,10 +107,12 @@ public class XmlSchemaGenerator { private String genDate; private String license; private boolean annotations; + private ProfileUtilities profileUtilities; public XmlSchemaGenerator(String folder, IWorkerContext context) { this.folder = folder; this.context = context; + this.profileUtilities = new ProfileUtilities(context, null, null); } public boolean isSingle() { @@ -325,12 +327,12 @@ public class XmlSchemaGenerator { ln(" "); // hack.... - for (ElementDefinition edc : ProfileUtilities.getChildList(sd, ed)) { + for (ElementDefinition edc : profileUtilities.getChildList(sd, ed)) { if (!(edc.hasRepresentation(PropertyRepresentation.XMLATTR) || edc.hasRepresentation(PropertyRepresentation.XMLTEXT)) && !inheritedElement(edc)) produceElement(sd, ed, edc, lang); } ln(" "); - for (ElementDefinition edc : ProfileUtilities.getChildList(sd, ed)) { + for (ElementDefinition edc : profileUtilities.getChildList(sd, ed)) { if ((edc.hasRepresentation(PropertyRepresentation.XMLATTR) || edc.hasRepresentation(PropertyRepresentation.XMLTEXT)) && !inheritedElement(edc)) produceAttribute(sd, ed, edc, lang); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java index 32dc778f5..59a6c824a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ObjectConverter.java @@ -46,9 +46,11 @@ import org.hl7.fhir.r5.model.DataType; public class ObjectConverter { private IWorkerContext context; + private ProfileUtilities profileUtilities; public ObjectConverter(IWorkerContext context) { this.context = context; + profileUtilities = new ProfileUtilities(context, null, null); } public Element convert(Resource ig) throws IOException, FHIRException { @@ -76,7 +78,7 @@ public class ObjectConverter { if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) res.setValue(((PrimitiveType) base).asStringValue()); - List children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); + List children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); for (ElementDefinition child : children) { String n = tail(child.getPath()); if (sd.getKind() != StructureDefinitionKind.PRIMITIVETYPE || !"value".equals(n)) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java index b1734497e..08a4b1558 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Property.java @@ -44,12 +44,14 @@ public class Property { private IWorkerContext context; private ElementDefinition definition; private StructureDefinition structure; - private Boolean canBePrimitive; + private Boolean canBePrimitive; + private ProfileUtilities profileUtilities; public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) { this.context = context; this.definition = definition; this.structure = structure; + profileUtilities = new ProfileUtilities(context, null, null); } public String getName() { @@ -248,7 +250,7 @@ public class Property { protected List getChildProperties(String elementName, String statedType) throws FHIRException { ElementDefinition ed = definition; StructureDefinition sd = structure; - List children = ProfileUtilities.getChildMap(sd, ed); + List children = profileUtilities.getChildMap(sd, ed); String url = null; if (children.isEmpty() || isElementWithOnlyExtension(ed, children)) { // ok, find the right definitions @@ -313,7 +315,7 @@ public class Property { sd = context.fetchResource(StructureDefinition.class, url); if (sd == null) throw new DefinitionException("Unable to find type '"+t+"' for name '"+elementName+"' on property "+definition.getPath()); - children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); + children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); } } List properties = new ArrayList(); @@ -326,7 +328,7 @@ public class Property { protected List getChildProperties(TypeDetails type) throws DefinitionException { ElementDefinition ed = definition; StructureDefinition sd = structure; - List children = ProfileUtilities.getChildMap(sd, ed); + List children = profileUtilities.getChildMap(sd, ed); if (children.isEmpty()) { // ok, find the right definitions String t = null; @@ -352,7 +354,7 @@ public class Property { sd = context.fetchResource(StructureDefinition.class, t); if (sd == null) throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath()); - children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); + children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); } } List properties = new ArrayList(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 2616da0f1..67b995299 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -152,6 +152,7 @@ public class FHIRPathEngine { private Map allTypes = new HashMap(); private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true private ValidationOptions terminologyServiceOptions = new ValidationOptions(); + private ProfileUtilities profileUtilities; // if the fhir path expressions are allowed to use constants beyond those defined in the specification // the application can implement them by providing a constant resolver @@ -249,6 +250,7 @@ public class FHIRPathEngine { public FHIRPathEngine(IWorkerContext worker) { super(); this.worker = worker; + profileUtilities = new ProfileUtilities(worker, null, null); for (StructureDefinition sd : worker.getStructures()) { if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() != StructureDefinitionKind.LOGICAL) allTypes.put(sd.getName(), sd); @@ -4204,14 +4206,14 @@ public class FHIRPathEngine { focus = element; } else { List childDefinitions; - childDefinitions = ProfileUtilities.getChildMap(sd, element); + childDefinitions = profileUtilities.getChildMap(sd, element); // if that's empty, get the children of the type if (childDefinitions.isEmpty()) { sd = fetchStructureByType(element); if (sd == null) throw new DefinitionException("Problem with use of resolve() - profile '"+element.getType().get(0).getProfile()+"' on "+element.getId()+" could not be resolved"); - childDefinitions = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); + childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); } for (ElementDefinition t : childDefinitions) { if (tailMatches(t, expr.getName())) { @@ -4237,14 +4239,14 @@ public class FHIRPathEngine { } else if ("extension".equals(expr.getName())) { String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue(); // targetUrl = targetUrl.substring(1,targetUrl.length()-1); - List childDefinitions = ProfileUtilities.getChildMap(sd, element); + List childDefinitions = profileUtilities.getChildMap(sd, element); for (ElementDefinition t : childDefinitions) { if (t.getPath().endsWith(".extension") && t.hasSliceName()) { StructureDefinition exsd = worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue()); while (exsd!=null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition()); if (exsd.getUrl().equals(targetUrl)) { - if (ProfileUtilities.getChildMap(sd, t).isEmpty()) + if (profileUtilities.getChildMap(sd, t).isEmpty()) sd = exsd; focus = t; break; @@ -4269,7 +4271,7 @@ public class FHIRPathEngine { } private ElementDefinition pickMandatorySlice(StructureDefinition sd, ElementDefinition element) throws DefinitionException { - List list = ProfileUtilities.getSliceList(sd, element); + List list = profileUtilities.getSliceList(sd, element); for (ElementDefinition ed : list) { if (ed.getMin() > 0) return ed; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java index e1e80c49e..1512f5792 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java @@ -56,10 +56,12 @@ public class GraphQLSchemaGenerator { private static final String INNER_TYPE_NAME = "gql.type.name"; IWorkerContext context; + private ProfileUtilities profileUtilities; public GraphQLSchemaGenerator(IWorkerContext context) { super(); this.context = context; + profileUtilities = new ProfileUtilities(context, null, null); } public void generateTypes(OutputStream stream) throws IOException, FHIRException { @@ -265,7 +267,7 @@ public class GraphQLSchemaGenerator { } private void generateProperties(List list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException { - List children = ProfileUtilities.getChildList(sd, ed); + List children = profileUtilities.getChildList(sd, ed); for (ElementDefinition child : children) { if (child.hasContentReference()) { ElementDefinition ref = resolveContentReference(sd, child.getContentReference()); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index 117451d19..93e2f0a44 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -482,7 +482,7 @@ public class NarrativeGenerator implements INarrativeGenerator { @Override public List children() { if (list == null) { - children = ProfileUtilities.getChildList(structure, definition); + children = profileUtilities.getChildList(structure, definition); list = new ArrayList(); for (ElementDefinition child : children) { List elements = new ArrayList(); @@ -632,7 +632,7 @@ public class NarrativeGenerator implements INarrativeGenerator { @Override public List children() { if (list == null) { - children = ProfileUtilities.getChildList(structure, definition); + children = profileUtilities.getChildList(structure, definition); list = new ArrayList(); for (ElementDefinition child : children) { List elements = new ArrayList(); @@ -703,7 +703,7 @@ public class NarrativeGenerator implements INarrativeGenerator { @Override public List children() { if (list2 == null) { - List children = ProfileUtilities.getChildList(definition, definition.getSnapshot().getElement().get(0)); + List children = profileUtilities.getChildList(definition, definition.getSnapshot().getElement().get(0)); list2 = new ArrayList(); for (ElementDefinition child : children) { List elements = new ArrayList(); @@ -841,7 +841,7 @@ public class NarrativeGenerator implements INarrativeGenerator { @Override public List children() { if (list2 == null) { - List children = ProfileUtilities.getChildList(definition, definition.getSnapshot().getElement().get(0)); + List children = profileUtilities.getChildList(definition, definition.getSnapshot().getElement().get(0)); list2 = new ArrayList(); for (ElementDefinition child : children) { List elements = new ArrayList(); @@ -1044,6 +1044,7 @@ public class NarrativeGenerator implements INarrativeGenerator { private ValidationOptions terminologyServiceOptions = new ValidationOptions(); private boolean noSlowLookup; private List codeSystemPropList = new ArrayList<>(); + private ProfileUtilities profileUtilities; public NarrativeGenerator(String prefix, String basePath, IWorkerContext context) { super(); @@ -1077,6 +1078,7 @@ public class NarrativeGenerator implements INarrativeGenerator { private void init() { + profileUtilities = new ProfileUtilities(context, null, null); renderingMaps.add(new ConceptMapRenderInstructions("Canonical Status", "http://hl7.org/fhir/ValueSet/resource-status", false)); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java index c70ccaf5a..6b4c1c29b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java @@ -136,10 +136,12 @@ public class QuestionnaireBuilder { // processing the response. for technical reasons, we still go through the process, but // we don't do the intensive parts of the work (save time) private Questionnaire prebuiltQuestionnaire; + private ProfileUtilities profileUtilities; public QuestionnaireBuilder(IWorkerContext context) { super(); this.context = context; + profileUtilities = new ProfileUtilities(context, null, null); } public Resource getReference() { @@ -291,7 +293,7 @@ public class QuestionnaireBuilder { } // now, we iterate the children - List list = ProfileUtilities.getChildList(profile, element); + List list = profileUtilities.getChildList(profile, element); for (ElementDefinition child : list) { if (!isExempt(element, child) && !parents.contains(child)) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java index a9ca48523..9d518844d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java @@ -229,7 +229,8 @@ public class StructureMapUtilities { private ITransformerServices services; private ProfileKnowledgeProvider pkp; private Map ids = new HashMap(); - private ValidationOptions terminologyServiceOptions = new ValidationOptions(); + private ValidationOptions terminologyServiceOptions = new ValidationOptions(); + private ProfileUtilities profileUtilities; public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) { super(); @@ -238,6 +239,7 @@ public class StructureMapUtilities { this.pkp = pkp; fpe = new FHIRPathEngine(worker); fpe.setHostServices(new FFHIRPathHostServices()); + profileUtilities = new ProfileUtilities(worker, null, null); } public StructureMapUtilities(IWorkerContext worker, ITransformerServices services) { @@ -246,6 +248,7 @@ public class StructureMapUtilities { this.services = services; fpe = new FHIRPathEngine(worker); fpe.setHostServices(new FFHIRPathHostServices()); + profileUtilities = new ProfileUtilities(worker, null, null); } public StructureMapUtilities(IWorkerContext worker) { @@ -253,6 +256,8 @@ public class StructureMapUtilities { this.worker = worker; fpe = new FHIRPathEngine(worker); fpe.setHostServices(new FFHIRPathHostServices()); + profileUtilities = new ProfileUtilities(worker, null, null); + } public static String render(StructureMap map) { @@ -2934,7 +2939,7 @@ public class StructureMapUtilities { private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException { boolean first = true; - List children = ProfileUtilities.getChildMap(sd, ed); + List children = profileUtilities.getChildMap(sd, ed); for (ElementDefinition child : children) { if (first && inner) { b.append(" then {\r\n"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index b0343a234..f6a1dd599 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -369,11 +369,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private ValidatorHostServices validatorServices; private boolean assumeValidRestReferences; private boolean allowExamples; + private ProfileUtilities profileUtilities; public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { super(theContext); this.context = theContext; this.externalHostServices = hostServices; + this.profileUtilities = new ProfileUtilities(theContext, null, null); fpe = new FHIRPathEngine(context); validatorServices = new ValidatorHostServices(); fpe.setHostServices(validatorServices); @@ -4570,7 +4572,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getUrl(), definition.getSliceName(), null); // get the list of direct defined children, including slices - List childDefinitions = ProfileUtilities.getChildMap(profile, definition); + List childDefinitions = profileUtilities.getChildMap(profile, definition); if (childDefinitions.isEmpty()) { if (actualType == null) return; // there'll be an error elsewhere in this case, and we're going to stop. @@ -4625,7 +4627,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat throw new DefinitionException(formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType)); trackUsage(dt, hostContext, element); - childDefinitions = ProfileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); + childDefinitions = profileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0)); return childDefinitions; } @@ -4926,7 +4928,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat int count = 0; List slices = null; if (ed.hasSlicing()) - slices = ProfileUtilities.getSliceList(profile, ed); + slices = profileUtilities.getSliceList(profile, ed); for (ElementInfo ei : children) if (ei.definition == ed) count++; From ca240e465986b01100f7667ea60bb907ce16d2b9 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 08:37:36 +1100 Subject: [PATCH 38/52] fix for $145 -proxy Flag treated as File to validate --- .../src/main/java/org/hl7/fhir/validation/Validator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java index 3b0913127..9cd53ac34 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java @@ -423,6 +423,8 @@ public class Validator { throw new Error("Specified -output without indicating output file"); else output = args[++i]; + } else if (args[i].equals("-proxy")) { + i++; // ignore next parameter } else if (args[i].equals("-profile")) { String p = null; if (i+1 == args.length) From c86fc8bbb5c59b86a7efa36d9445abd156be0940 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 21:24:51 +1100 Subject: [PATCH 39/52] improve codesystem property rendering --- .../r5/terminologies/CodeSystemUtilities.java | 44 +++++++++++++++++++ .../hl7/fhir/r5/utils/NarrativeGenerator.java | 40 +++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java index 7120e1cd7..7db44a8bb 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java @@ -372,4 +372,48 @@ public class CodeSystemUtilities { return false; } + public static boolean hasCode(CodeSystem cs, String code) { + for (ConceptDefinitionComponent cc : cs.getConcept()) { + if (hasCode(cc, code)) { + return true; + } + } + return false; + } + + private static boolean hasCode(ConceptDefinitionComponent cc, String code) { + if (code.equals(cc.getCode())) { + return true; + } + for (ConceptDefinitionComponent c : cc.getConcept()) { + if (hasCode(c, code)) { + return true; + } + } + return false; + } + + public static ConceptDefinitionComponent getCode(CodeSystem cs, String code) { + for (ConceptDefinitionComponent cc : cs.getConcept()) { + ConceptDefinitionComponent cd = getCode(cc, code); + if (cd != null) { + return cd; + } + } + return null; + } + + private static ConceptDefinitionComponent getCode(ConceptDefinitionComponent cc, String code) { + if (code.equals(cc.getCode())) { + return cc; + } + for (ConceptDefinitionComponent c : cc.getConcept()) { + ConceptDefinitionComponent cd = getCode(c, code); + if (cd != null) { + return cd; + } + } + return null; + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index 93e2f0a44..61f29a595 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -2935,19 +2935,44 @@ public class NarrativeGenerator implements INarrativeGenerator { return true; } String uri = cp.getUri(); + String code = null; if (Utilities.noString(uri)){ return false; } if (uri.contains("#")) { + code = uri.substring(uri.indexOf("#")+1); uri = uri.substring(0, uri.indexOf("#")); } - return - Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || - codeSystemPropList.contains(uri); + if (Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || codeSystemPropList.contains(uri)) { + return true; + }; + CodeSystem cs = context.fetchCodeSystem(uri); + if (cs == null) { + return false; + } + return CodeSystemUtilities.hasCode(cs, code); } return false; } + private String getDisplayForProperty(String uri) { + if (Utilities.noString(uri)){ + return null; + } + String code = null; + if (uri.contains("#")) { + code = uri.substring(uri.indexOf("#")+1); + uri = uri.substring(0, uri.indexOf("#")); + } + CodeSystem cs = context.fetchCodeSystem(uri); + if (cs == null) { + return null; + } + ConceptDefinitionComponent cc = CodeSystemUtilities.getCode(cs, code); + return cc == null ? null : cc.getDisplay(); + } + + private int countConcepts(List list) { int count = list.size(); for (ConceptDefinitionComponent c : list) @@ -3475,7 +3500,14 @@ public class NarrativeGenerator implements INarrativeGenerator { tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Version", lang)); if (properties != null) { for (PropertyComponent pc : properties) { - tr.td().b().tx(context.translator().translate("xhtml-gen-cs", ToolingExtensions.getPresentation(pc, pc.getCodeElement()), lang)); + String display = ToolingExtensions.getPresentation(pc, pc.getCodeElement()); + if (display == null || display.equals(pc.getCode()) && pc.hasUri()) { + display = getDisplayForProperty(pc.getUri()); + if (display == null) { + display = pc.getCode(); + } + } + tr.td().b().tx(context.translator().translate("xhtml-gen-cs", display, lang)); } } return tr; From 65fd77ed76b778b9cf5d0c912f619fed42419911 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 21:27:37 +1100 Subject: [PATCH 40/52] Fix version issue in validation engine + handle loading latest version of a package --- .../utilities/cache/PackageCacheManager.java | 25 +- .../fhir/utilities/cache/PackageClient.java | 17 + .../hl7/fhir/validation/ValidationEngine.java | 342 +++++++++++++++++- .../org/hl7/fhir/validation/Validator.java | 44 ++- .../tests/ProfileComparisonTests.java | 2 +- .../tests/ValidationEngineTests.java | 14 +- .../validation/tests/ValidationTestSuite.java | 10 +- pom.xml | 2 +- 8 files changed, 420 insertions(+), 36 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java index 362206220..4e4aa6503 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageCacheManager.java @@ -84,9 +84,12 @@ public class PackageCacheManager { public InputStream stream; public String url; - public InputStreamWithSrc(InputStream stream, String url) { + public String version; + + public InputStreamWithSrc(InputStream stream, String url, String version) { this.stream = stream; this.url = url; + this.version = version; } } @@ -316,10 +319,16 @@ public class PackageCacheManager { PackageClient pc = new PackageClient(PRIMARY_SERVER); InputStream stream; try { + if (Utilities.noString(v)) { + v = pc.getLatestVersion(id); + } stream = pc.fetch(id, v); } catch (IOException e) { pc = new PackageClient(SECONDARY_SERVER); try { + if (Utilities.noString(v)) { + v = pc.getLatestVersion(id); + } stream = pc.fetch(id, v); } catch (IOException e1) { // ok, well, we'll try the old way @@ -329,7 +338,7 @@ public class PackageCacheManager { } } } - return new InputStreamWithSrc(stream, pc.url(id, v)); + return new InputStreamWithSrc(stream, pc.url(id, v), v); } @@ -547,12 +556,8 @@ public class PackageCacheManager { } public NpmPackage loadPackage(String id, String v) throws FHIRException, IOException { - if (Utilities.noString(v)) { - throw new FHIRException("Invalid version - ''"); - } - //ok, try to resolve locally - if (v.startsWith("file:")) { + if (!Utilities.noString(v) && v.startsWith("file:")) { return loadPackageFromFile(id, v.substring(5)); } NpmPackage p = loadPackageFromCacheOnly(id, v); @@ -580,7 +585,7 @@ public class PackageCacheManager { } else { source = loadFromPackageServer(id, v); } - return addPackageToCache(id, v, source.stream, source.url); + return addPackageToCache(id, v == null ? source.version : v, source.stream, source.url); } @@ -607,10 +612,10 @@ public class PackageCacheManager { checkBuildLoaded(); if (ciList.containsKey(id)) { InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(ciList.get(id), "package.tgz"), false); - return new InputStreamWithSrc(stream, Utilities.pathURL(ciList.get(id), "package.tgz")); + return new InputStreamWithSrc(stream, Utilities.pathURL(ciList.get(id), "package.tgz"), "current"); } else if (id.startsWith("hl7.fhir.r5")) { InputStream stream = fetchFromUrlSpecific(Utilities.pathURL("http://hl7.org/fhir/2020Feb", id+".tgz"), false); - return new InputStreamWithSrc(stream, Utilities.pathURL("http://hl7.org/fhir/2020Feb", id+".tgz")); + return new InputStreamWithSrc(stream, Utilities.pathURL("http://hl7.org/fhir/2020Feb", id+".tgz"), "current"); } else { throw new FHIRException("The package '"+id+"' has not entry on the current build server"); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java index df04013bb..cfbb58e74 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java @@ -11,9 +11,11 @@ import java.util.Date; import java.util.List; import java.util.Set; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo; import org.hl7.fhir.utilities.json.JSONUtil; @@ -173,6 +175,21 @@ public class PackageClient { public String url(String id, String v) { return Utilities.pathURL(address, id, v); } + + public String getLatestVersion(String id) throws IOException { + List list = getVersions(id); + if (list.isEmpty()) { + throw new IOException("Package not found: "+id); + } else { + String v = list.get(0).version; + for (PackageInfo p : list) { + if (VersionUtilities.isThisOrLater(v, p.version)) { + v = p.version; + } + } + return v; + } + } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index ecf94ccc9..4fda919e7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -309,17 +309,19 @@ public class ValidationEngine implements IValidatorResourceFetcher { this.anyExtensionsAllowed = anyExtensionsAllowed; } - public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, boolean canRunWithoutTerminologyServer) throws Exception { + public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, boolean canRunWithoutTerminologyServer, String vString) throws Exception { pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); loadInitialDefinitions(src); context.setCanRunWithoutTerminology(canRunWithoutTerminologyServer); setTerminologyServer(txsrvr, txLog, version); + this.version = vString; } - public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version) throws Exception { + public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, String vString) throws Exception { pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION); loadInitialDefinitions(src); setTerminologyServer(txsrvr, txLog, version); + this.version = vString; } public ValidationEngine(String src) throws Exception { @@ -1206,7 +1208,7 @@ public class ValidationEngine implements IValidatorResourceFetcher { } if(structureDefinition == null) - throw new FHIRException("Unable to determine StructureDefinition for target type"); + throw new FHIRException("Unable to find StructureDefinition for target type ('"+targetTypeUrl+"')"); return Manager.build(getContext(), structureDefinition); } @@ -1691,6 +1693,338 @@ public class ValidationEngine implements IValidatorResourceFetcher { this.assumeValidRestReferences = assumeValidRestReferences; } + public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception { + Content cnt = loadContent(source, "validate"); + org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); + + // if the src has a url, we try to use the java code + if ((canDoNative == null && src.hasChild("url")) || (canDoNative != null && canDoNative)) { + try { + if (VersionUtilities.isR2Ver(version)) { + return convertVersionNativeR2(targetVer, cnt, format); + } else if (VersionUtilities.isR2BVer(version)) { + return convertVersionNativeR2b(targetVer, cnt, format); + } else if (VersionUtilities.isR3Ver(version)) { + return convertVersionNativeR3(targetVer, cnt, format); + } else if (VersionUtilities.isR4Ver(version)) { + return convertVersionNativeR4(targetVer, cnt, format); + } else { + throw new Exception("Source version not supported yet: "+version); + } + } catch (Exception e) { + System.out.println("Conversion failed using Java convertor: "+e.getMessage()); + } + } + // ok, we try converting using the structure maps + System.out.println("Loading hl7.fhir.xver.r4"); + loadIg("hl7.fhir.xver.r4", false); + String type = src.fhirType(); + String url = getMapId(type, targetVer); + List outputs = new ArrayList(); + StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs)); + StructureMap map = context.getTransform(url); + if (map == null) + throw new Error("Unable to find map "+url+" (Known Maps = "+context.listMapUrls()+")"); + org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map); + scu.transform(null, src, map, resource); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + Manager.compose(context, resource, bs, format, OutputStyle.PRETTY, null); + return bs.toByteArray(); + } + + private String getMapId(String type, String targetVer) { + if (VersionUtilities.isR2Ver(version)) { + if (VersionUtilities.isR3Ver(targetVer)) { + return "http://hl7.org/fhir/StructureMap/"+type+"2to3"; + } + } else if (VersionUtilities.isR3Ver(version)) { + if (VersionUtilities.isR2Ver(targetVer)) { + return "http://hl7.org/fhir/StructureMap/"+type+"3to2"; + } else if (VersionUtilities.isR4Ver(targetVer)) { + return "http://hl7.org/fhir/StructureMap/"+type+"3to4"; + } + } else if (VersionUtilities.isR4Ver(version)) { + if (VersionUtilities.isR3Ver(targetVer)) { + return "http://hl7.org/fhir/StructureMap/"+type+"4to3"; + } + } + throw new FHIRException("Source/Target version not supported: "+version+" -> "+targetVer); + } -} + public byte[] convertVersionNativeR2(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { + org.hl7.fhir.dstu2.model.Resource r2; + switch (cnt.cntType) { + case JSON: + r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(cnt.focus); + break; + case XML: + r2 = new org.hl7.fhir.dstu2.formats.XmlParser().parse(cnt.focus); + break; + default: + throw new FHIRException("Unsupported input format: "+cnt.cntType.toString()); + } + if (VersionUtilities.isR2Ver(targetVer)) { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2.formats.JsonParser().compose(bs, r2); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2.formats.XmlParser().compose(bs, r2); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR2BVer(targetVer)) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertor_10_30.convertResource(r2); + org.hl7.fhir.dstu2016may.model.Resource r2b = VersionConvertor_14_30.convertResource(r3); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2016may.formats.JsonParser().compose(bs, r2b); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2016may.formats.XmlParser().compose(bs, r2b); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR3Ver(targetVer)) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertor_10_30.convertResource(r2); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu3.formats.JsonParser().compose(bs, r3); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu3.formats.XmlParser().compose(bs, r3); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR4Ver(targetVer)) { + org.hl7.fhir.r4.model.Resource r4 = VersionConvertor_10_40.convertResource(r2); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.r4.formats.JsonParser().compose(bs, r4); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.r4.formats.XmlParser().compose(bs, r4); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else { + throw new Exception("Target Version not supported yet: "+targetVer); + } + } + + public byte[] convertVersionNativeR2b(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { + org.hl7.fhir.dstu2016may.model.Resource r2b; + switch (cnt.cntType) { + case JSON: + r2b = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(cnt.focus); + break; + case XML: + r2b = new org.hl7.fhir.dstu2016may.formats.XmlParser().parse(cnt.focus); + break; + default: + throw new FHIRException("Unsupported input format: "+cnt.cntType.toString()); + } + if (VersionUtilities.isR2Ver(targetVer)) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertor_14_30.convertResource(r2b); + org.hl7.fhir.dstu2.model.Resource r2 = VersionConvertor_10_30.convertResource(r3); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2.formats.JsonParser().compose(bs, r2); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2.formats.XmlParser().compose(bs, r2); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR2BVer(targetVer)) { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2016may.formats.JsonParser().compose(bs, r2b); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2016may.formats.XmlParser().compose(bs, r2b); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR3Ver(targetVer)) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertor_14_30.convertResource(r2b); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu3.formats.JsonParser().compose(bs, r3); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu3.formats.XmlParser().compose(bs, r3); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR4Ver(targetVer)) { + org.hl7.fhir.r4.model.Resource r4 = VersionConvertor_14_40.convertResource(r2b); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.r4.formats.JsonParser().compose(bs, r4); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.r4.formats.XmlParser().compose(bs, r4); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else { + throw new Exception("Target Version not supported yet: "+targetVer); + } + } + + public byte[] convertVersionNativeR3(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { + org.hl7.fhir.dstu3.model.Resource r3; + switch (cnt.cntType) { + case JSON: + r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(cnt.focus); + break; + case XML: + r3 = new org.hl7.fhir.dstu3.formats.XmlParser().parse(cnt.focus); + break; + default: + throw new FHIRException("Unsupported input format: "+cnt.cntType.toString()); + } + if (VersionUtilities.isR2Ver(targetVer)) { + org.hl7.fhir.dstu2.model.Resource r2 = VersionConvertor_10_30.convertResource(r3); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2.formats.JsonParser().compose(bs, r2); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2.formats.XmlParser().compose(bs, r2); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR2BVer(targetVer)) { + org.hl7.fhir.dstu2016may.model.Resource r2b = VersionConvertor_14_30.convertResource(r3); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2016may.formats.JsonParser().compose(bs, r2b); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2016may.formats.XmlParser().compose(bs, r2b); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR3Ver(targetVer)) { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu3.formats.JsonParser().compose(bs, r3); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu3.formats.XmlParser().compose(bs, r3); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR4Ver(targetVer)) { + org.hl7.fhir.r4.model.Resource r4 = VersionConvertor_30_40.convertResource(r3, false); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.r4.formats.JsonParser().compose(bs, r4); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.r4.formats.XmlParser().compose(bs, r4); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else { + throw new Exception("Target Version not supported yet: "+targetVer); + } + } + + + public byte[] convertVersionNativeR4(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { + org.hl7.fhir.r4.model.Resource r4; + switch (cnt.cntType) { + case JSON: + r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(cnt.focus); + break; + case XML: + r4 = new org.hl7.fhir.r4.formats.XmlParser().parse(cnt.focus); + break; + default: + throw new FHIRException("Unsupported input format: "+cnt.cntType.toString()); + } + if (VersionUtilities.isR2Ver(targetVer)) { + org.hl7.fhir.dstu2.model.Resource r2 = VersionConvertor_10_40.convertResource(r4); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2.formats.JsonParser().compose(bs, r2); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2.formats.XmlParser().compose(bs, r2); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR2BVer(targetVer)) { + org.hl7.fhir.dstu2016may.model.Resource r2b = VersionConvertor_14_40.convertResource(r4); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu2016may.formats.JsonParser().compose(bs, r2b); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu2016may.formats.XmlParser().compose(bs, r2b); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR3Ver(targetVer)) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertor_30_40.convertResource(r4, false); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.dstu3.formats.JsonParser().compose(bs, r3); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.dstu3.formats.XmlParser().compose(bs, r3); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else if (VersionUtilities.isR4Ver(targetVer)) { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + switch (format) { + case JSON: + new org.hl7.fhir.r4.formats.JsonParser().compose(bs, r4); + return bs.toByteArray(); + case XML: + new org.hl7.fhir.r4.formats.XmlParser().compose(bs, r4); + return bs.toByteArray(); + default: + throw new FHIRException("Unsupported output format: "+cnt.cntType.toString()); + } + } else { + throw new Exception("Target Version not supported yet: "+targetVer); + } + } + +} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java index 9cd53ac34..408ed8739 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java @@ -62,6 +62,7 @@ import java.util.UUID; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities.CapabilityStatementComparisonOutput; import org.hl7.fhir.r5.conformance.ProfileComparer; +import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.XmlParser; @@ -105,7 +106,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage; public class Validator { public enum EngineMode { - VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH + VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION } private static String getNamedParam(String[] args, String param) { @@ -156,7 +157,7 @@ public class Validator { System.out.println("If requested, instances will also be verified against the appropriate schema"); System.out.println("W3C XML Schema, JSON schema or ShEx, as appropriate"); System.out.println(""); - System.out.println("Usage: org.hl7.fhir.r5.validation.ValidationEngine (parameters)"); + System.out.println("Usage: java -jar [validator].jar (parameters)"); System.out.println(""); System.out.println("The following parameters are supported:"); System.out.println("[source]: a file, url, directory or pattern for resources to validate. At"); @@ -316,7 +317,7 @@ public class Validator { } String definitions = VersionUtilities.packageForVersion(v)+"#"+v; System.out.println("Loading (v = "+v+", tx server http://tx.fhir.org)"); - ValidationEngine validator = new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v)); + ValidationEngine validator = new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v), v); for (int i = 0; i < args.length; i++) { if ("-ig".equals(args[i])) { if (i+1 == args.length) @@ -325,7 +326,6 @@ public class Validator { String s = args[++i]; if (!s.startsWith("hl7.fhir.core-")) { System.out.println("Load Package: "+s); - validator.loadIg(s, true); } } } @@ -402,6 +402,7 @@ public class Validator { List profiles = new ArrayList(); EngineMode mode = EngineMode.VALIDATION; String output = null; + Boolean canDoNative = null; List sources= new ArrayList(); Map locations = new HashMap(); String sv = "current"; @@ -410,6 +411,7 @@ public class Validator { String lang = null; String fhirpath = null; String snomedCT = "900000000000207008"; + String targetVer = null; boolean doDebug = false; boolean assumeValidRestReferences = false; @@ -479,6 +481,13 @@ public class Validator { anyExtensionsAllowed = false; } else if (args[i].equals("-hintAboutNonMustSupport")) { hintAboutNonMustSupport = true; + } else if (args[i].equals("-to-version")) { + targetVer = args[++i]; + mode = EngineMode.VERSION; + } else if (args[i].equals("-do-native")) { + canDoNative = true; + } else if (args[i].equals("-no-native")) { + canDoNative = false; } else if (args[i].equals("-transform")) { map = args[++i]; mode = EngineMode.TRANSFORM; @@ -564,11 +573,9 @@ public class Validator { String definitions = VersionUtilities.packageForVersion(sv)+"#"+VersionUtilities.getCurrentVersion(sv); System.out.println(" .. FHIR Version "+sv+", definitions from "+definitions); System.out.println(" .. connect to tx server @ "+txServer); - ValidationEngine validator = new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(sv)); + ValidationEngine validator = new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(sv), sv); validator.setDebug(doDebug); System.out.println(" (v"+validator.getContext().getVersion()+")"); - if (sv != null) - validator.setVersion(sv); for (String src : igs) { System.out.println("+ .. load IG from "+src); validator.loadIg(src, recursive); @@ -588,7 +595,28 @@ public class Validator { x = new XmlParser(); x.setOutputStyle(OutputStyle.PRETTY); - if (mode == EngineMode.TRANSFORM) { + if (mode == EngineMode.VERSION) { + if (sources.size() > 1) { + throw new Exception("Can only have one source when converting versions (found "+sources+")"); + } + if (targetVer == null) { + throw new Exception("Must provide a map when converting versions"); + } + if (output == null) { + throw new Exception("Must nominate an output when converting versions"); + } + try { + if (mapLog != null) { + validator.setMapLog(mapLog); + } + byte[] r = validator.transformVersion(sources.get(0), targetVer, output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML, canDoNative); + System.out.println(" ...success"); + TextFile.bytesToFile(r, output); + } catch (Exception e) { + System.out.println(" ...Failure: "+e.getMessage()); + e.printStackTrace(); + } + } else if (mode == EngineMode.TRANSFORM) { if (sources.size() > 1) throw new Exception("Can only have one source when doing a transform (found "+sources+")"); if (txServer == null) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ProfileComparisonTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ProfileComparisonTests.java index c2607a462..a25485be3 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ProfileComparisonTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ProfileComparisonTests.java @@ -20,7 +20,7 @@ public class ProfileComparisonTests { public void testCurrentComparison() throws Exception { if (!TestUtilities.silent) System.out.println("Compare US Patient Core with AU Patient Base"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.R4); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, "3.0.2"); ve.loadIg("hl7.fhir.us.core#1.0.1", false); ve.loadIg("hl7.fhir.au.base#dev", false); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java index 4c662240b..4aecd05ea 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java @@ -25,7 +25,7 @@ public class ValidationEngineTests { public void testCurrentXml() throws Exception { if (!TestUtilities.silent) System.out.println("Validate patient-example.xml in Current version"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4, "4.0.1"); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient-example.xml"), null); int e = errors(op); int w = warnings(op); @@ -45,7 +45,7 @@ public class ValidationEngineTests { public void testCurrentJson() throws Exception { if (!TestUtilities.silent) System.out.println("Validate patient-example.json in Current version"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4, "4.0.1"); OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "patient-example.json"), null); int e = errors(op); int w = warnings(op); @@ -65,7 +65,7 @@ public class ValidationEngineTests { } if (!TestUtilities.silent) System.out.println("Validate patient-example.xml in v1.4.0 version"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, null, FhirPublication.DSTU2016May); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, null, FhirPublication.DSTU2016May, "1.4.0"); ve.setNoInvariantChecks(true); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient140.xml"), null); if (!TestUtilities.silent) @@ -90,7 +90,7 @@ public class ValidationEngineTests { } if (!org.hl7.fhir.validation.tests.utilities.TestUtilities.silent) System.out.println("Validate patient-example.xml in v1.0.2 version"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, "41.0.2"); ve.setNoInvariantChecks(true); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient102.xml"), null); if (!TestUtilities.silent) @@ -115,7 +115,7 @@ public class ValidationEngineTests { } if (!TestUtilities.silent) System.out.println("Validate patient-example.xml in v1.0.2 version"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, "1.0.2"); ve.setNoInvariantChecks(true); OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation102.json"), null); if (!TestUtilities.silent) @@ -137,7 +137,7 @@ public class ValidationEngineTests { public void test301() throws Exception { if (!TestUtilities.silent) System.out.println("Validate observation301.xml against Core"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, "3.0.2"); if (!TestUtilities.silent) System.out.println(" .. load USCore"); OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "observation301.xml"), null); @@ -156,7 +156,7 @@ public class ValidationEngineTests { public void test301USCore() throws Exception { if (!TestUtilities.silent) System.out.println("Validate patient300.xml against US-Core"); - ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3); + ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, "3.0.2"); if (!TestUtilities.silent) System.out.println(" .. load USCore"); ve.loadIg("hl7.fhir.us.core#1.0.1", false); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 209954362..4006de2f9 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -105,15 +105,15 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour version = VersionUtilities.getMajMin(version); if (!ve.containsKey(version)) { if (version.startsWith("5.0")) - ve.put(version, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, txLog, FhirPublication.R5, true)); + ve.put(version, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, txLog, FhirPublication.R5, true, "4.2.0")); else if (version.startsWith("3.0")) - ve.put(version, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, txLog, FhirPublication.STU3, true)); + ve.put(version, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, txLog, FhirPublication.STU3, true, "3.0.2")); else if (version.startsWith("4.0")) - ve.put(version, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, txLog, FhirPublication.R4, true)); + ve.put(version, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, txLog, FhirPublication.R4, true, "4.0.1")); else if (version.startsWith("1.0")) - ve.put(version, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, txLog, FhirPublication.DSTU2, true)); + ve.put(version, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, txLog, FhirPublication.DSTU2, true, "1.0.2")); else if (version.startsWith("1.4")) - ve.put(version, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, txLog, FhirPublication.DSTU2016May, true)); + ve.put(version, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, txLog, FhirPublication.DSTU2016May, true, "1.4.0")); else throw new Exception("unknown version "+version); } diff --git a/pom.xml b/pom.xml index 18eca1e2e..65ae70118 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 4.2.0 - 1.0.44-SNAPSHOT + 1.0.45-SNAPSHOT org.hl7.fhir.core From 5d971f35e9781ce39dcd33066d75399d7e5c0428 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 3 Mar 2020 21:30:01 +1100 Subject: [PATCH 41/52] Release new version 4.2.5 --- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 2 +- release.bat | 4 ++-- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 386cb6075..16026130e 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 8798a7c21..5211191cc 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 6526c67cb..06ce6ac48 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 16696da7d..11a5e2379 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 7b7343c5b..0d3df0d68 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 6f7557d32..b2a6107dd 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 158b125b3..9e240001d 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index ae7a2e26f..2a28ae91c 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index ca3de0043..f603b1f0a 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 65ae70118..90d080ae2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ each other. It is fine to bump the point version of this POM without affecting HAPI FHIR. --> - 4.2.4-SNAPSHOT + 4.2.5-SNAPSHOT 4.2.0 diff --git a/release.bat b/release.bat index 36d29b406..60d869613 100644 --- a/release.bat +++ b/release.bat @@ -1,7 +1,7 @@ @echo off -set oldver=4.2.3 -set newver=4.2.4 +set oldver=4.2.4 +set newver=4.2.5 echo .. echo ========================================================================= From bdc473e10519bc1ce3aa77793b3746e308734423 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 4 Mar 2020 14:28:01 +1100 Subject: [PATCH 42/52] Support exists(criteria) --- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 67b995299..626edada1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -1051,7 +1051,7 @@ public class FHIRPathEngine { switch (exp.getFunction()) { case Empty: return checkParamCount(lexer, location, exp, 0); case Not: return checkParamCount(lexer, location, exp, 0); - case Exists: return checkParamCount(lexer, location, exp, 0); + case Exists: return checkParamCount(lexer, location, exp, 0, 1); case SubsetOf: return checkParamCount(lexer, location, exp, 1); case SupersetOf: return checkParamCount(lexer, location, exp, 1); case IsDistinct: return checkParamCount(lexer, location, exp, 0); @@ -2381,8 +2381,10 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Not : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); - case Exists : + case Exists : { + checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); + } case SubsetOf : { checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -3307,9 +3309,19 @@ public class FHIRPathEngine { private List funcExists(ExecutionContext context, List focus, ExpressionNode exp) { List result = new ArrayList(); boolean empty = true; - for (Base f : focus) - if (!f.isEmpty()) + List pc = new ArrayList(); + for (Base f : focus) { + if (exp.getParameters().size() == 1) { + pc.clear(); + pc.add(f); + Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true)); + if (v == Equality.True) { + empty = false; + } + } else if (!f.isEmpty()) { empty = false; + } + } result.add(new BooleanType(!empty).noExtensions()); return result; } From 323d933f76dc4b17f0777fd25afd9cb57e56d29b Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 4 Mar 2020 14:28:38 +1100 Subject: [PATCH 43/52] fix bug rendering CodeSystem properties --- .../hl7/fhir/r5/utils/NarrativeGenerator.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index 61f29a595..cb3c3b669 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -56,7 +56,6 @@ Copyright (c) 2011+, HL7, Inc */ import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -91,6 +90,7 @@ import org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntryResponseComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent; import org.hl7.fhir.r5.model.Bundle.BundleType; +import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; @@ -120,6 +120,7 @@ import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; import org.hl7.fhir.r5.model.ContactDetail; import org.hl7.fhir.r5.model.ContactPoint; import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem; +import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DiagnosticReport; import org.hl7.fhir.r5.model.DomainResource; @@ -127,6 +128,7 @@ import org.hl7.fhir.r5.model.Dosage; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship; +import org.hl7.fhir.r5.model.Enumerations.FilterOperator; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.ExtensionHelper; import org.hl7.fhir.r5.model.HumanName; @@ -136,7 +138,6 @@ import org.hl7.fhir.r5.model.Identifier; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.InstantType; import org.hl7.fhir.r5.model.Meta; -import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.Narrative; import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; import org.hl7.fhir.r5.model.OperationDefinition; @@ -163,7 +164,6 @@ import org.hl7.fhir.r5.model.Timing; import org.hl7.fhir.r5.model.Timing.EventTiming; import org.hl7.fhir.r5.model.Timing.TimingRepeatComponent; import org.hl7.fhir.r5.model.Timing.UnitsOfTime; -import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.ValueSet; @@ -171,8 +171,6 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; -import org.hl7.fhir.r5.model.Enumerations.FilterOperator; -import org.hl7.fhir.r5.model.Enumerations.ResourceTypeEnum; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; @@ -181,10 +179,8 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; -import org.hl7.fhir.r5.utils.NarrativeGenerator.ILiquidTemplateProvider; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationOptions; @@ -2934,6 +2930,9 @@ public class NarrativeGenerator implements INarrativeGenerator { if (cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) { return true; } + if (cp.getCodeElement().hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) { + return true; + } String uri = cp.getUri(); String code = null; if (Utilities.noString(uri)){ @@ -4127,6 +4126,9 @@ public class NarrativeGenerator implements INarrativeGenerator { } private ConceptDefinitionComponent getConceptForCode(CodeSystem e, String code, ConceptSetComponent inc) { + if (code == null) { + return null; + } // first, look in the code systems if (e == null) e = context.fetchCodeSystem(inc.getSystem()); From 30c325a8a49b1dfb3be9b4b4c5a2549fa9b091c0 Mon Sep 17 00:00:00 2001 From: Lloyd McKenzie Date: Wed, 4 Mar 2020 19:57:30 -0700 Subject: [PATCH 44/52] Added support for converting MedicinalProduct between R4 and R5 --- .../convertors/VersionConvertor_40_50.java | 4 + .../MedicinalProductDefinition40_50.java | 360 ++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/MedicinalProductDefinition40_50.java diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_40_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_40_50.java index 7fd48a877..7a7ac771e 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_40_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_40_50.java @@ -4090,6 +4090,8 @@ public class VersionConvertor_40_50 { return MedicationRequest40_50.convertMedicationRequest((org.hl7.fhir.r4.model.MedicationRequest) src); if (src instanceof org.hl7.fhir.r4.model.MedicationStatement) return MedicationStatement40_50.convertMedicationStatement((org.hl7.fhir.r4.model.MedicationStatement) src); + if (src instanceof org.hl7.fhir.r4.model.MedicinalProduct) + return MedicinalProductDefinition40_50.convertMedicinalProduct((org.hl7.fhir.r4.model.MedicinalProduct) src); if (src instanceof org.hl7.fhir.r4.model.MessageDefinition) return MessageDefinition40_50.convertMessageDefinition((org.hl7.fhir.r4.model.MessageDefinition) src); if (src instanceof org.hl7.fhir.r4.model.MessageHeader) @@ -4355,6 +4357,8 @@ public class VersionConvertor_40_50 { return MedicationRequest40_50.convertMedicationRequest((org.hl7.fhir.r5.model.MedicationRequest) src); if (src instanceof org.hl7.fhir.r5.model.MedicationUsage) return MedicationStatement40_50.convertMedicationStatement((org.hl7.fhir.r5.model.MedicationUsage) src); + if (src instanceof org.hl7.fhir.r5.model.MedicinalProductDefinition) + return MedicinalProductDefinition40_50.convertMedicinalProductDefinition((org.hl7.fhir.r5.model.MedicinalProductDefinition) src); if (src instanceof org.hl7.fhir.r5.model.MessageDefinition) return MessageDefinition40_50.convertMessageDefinition((org.hl7.fhir.r5.model.MessageDefinition) src); if (src instanceof org.hl7.fhir.r5.model.MessageHeader) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/MedicinalProductDefinition40_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/MedicinalProductDefinition40_50.java new file mode 100644 index 000000000..eb5c318cf --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/MedicinalProductDefinition40_50.java @@ -0,0 +1,360 @@ +package org.hl7.fhir.convertors.conv40_50; + +/*- + * #%L + * org.hl7.fhir.convertors + * %% + * Copyright (C) 2014 - 2019 Health Level 7 + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +import org.hl7.fhir.convertors.VersionConvertor_40_50; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.model.MarketingStatus; +import org.hl7.fhir.r5.model.CodeableReference; + +/* + 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 + list of conditions and the following disclaimer. + * 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 + 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. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + 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. + +*/ +// Generated on Sun, Feb 24, 2019 11:37+1100 for FHIR v4.0.0 +public class MedicinalProductDefinition40_50 extends VersionConvertor_40_50 { + + public static org.hl7.fhir.r5.model.MedicinalProductDefinition convertMedicinalProduct(org.hl7.fhir.r4.model.MedicinalProduct src) throws FHIRException { + if (src == null) + return null; + org.hl7.fhir.r5.model.MedicinalProductDefinition tgt = new org.hl7.fhir.r5.model.MedicinalProductDefinition(); + copyDomainResource(src, tgt); + for (org.hl7.fhir.r4.model.Identifier t : src.getIdentifier()) tgt.addIdentifier(convertIdentifier(t)); + if (src.hasType()) + tgt.setType(convertCodeableConcept(src.getType())); + if (src.hasDomain()) + tgt.setDomain(convertCoding(src.getDomain())); + // version (new) + // status (new) + // description (new) + if (src.hasCombinedPharmaceuticalDoseForm()) + tgt.setCombinedPharmaceuticalDoseForm(convertCodeableConcept(src.getCombinedPharmaceuticalDoseForm())); + // indication (new) + if (src.hasLegalStatusOfSupply()) + tgt.setLegalStatusOfSupply(convertCodeableConcept(src.getLegalStatusOfSupply())); + if (src.hasAdditionalMonitoringIndicator()) + tgt.setAdditionalMonitoringIndicator(convertCodeableConcept(src.getAdditionalMonitoringIndicator())); + for (org.hl7.fhir.r4.model.StringType t : src.getSpecialMeasures()) { + // specialMeasures string -> CodeableConcept + org.hl7.fhir.r5.model.CodeableConcept c = new org.hl7.fhir.r5.model.CodeableConcept(); + tgt.addSpecialMeasures(c); + c.setTextElement(convertString(t)); + } + if (src.hasPaediatricUseIndicator()) + tgt.setPaediatricUseIndicator(convertCodeableConcept(src.getPaediatricUseIndicator())); + for (org.hl7.fhir.r4.model.CodeableConcept t : src.getProductClassification()) tgt.addProductClassification(convertCodeableConcept(t)); + for (org.hl7.fhir.r4.model.MarketingStatus t : src.getMarketingStatus()) tgt.addMarketingStatus(convertMarketingStatus(t)); + for (org.hl7.fhir.r4.model.Reference t : src.getPharmaceuticalProduct()) tgt.addPharmaceuticalProduct(convertReference(t)); + for (org.hl7.fhir.r4.model.Reference t : src.getPackagedMedicinalProduct()) tgt.addPackagedMedicinalProduct(convertReference(t)); + // ingredient (new) + for (org.hl7.fhir.r4.model.Reference t : src.getAttachedDocument()) tgt.addAttachedDocument(convertReference(t)); + for (org.hl7.fhir.r4.model.Reference t : src.getMasterFile()) tgt.addMasterFile(convertReference(t)); + for (org.hl7.fhir.r4.model.Reference t : src.getContact()) { + // contact (Reference -> complex + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionContactComponent c = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionContactComponent(); + tgt.addContact(c); + c.setContact(convertReference(t)); + } + for (org.hl7.fhir.r4.model.Reference t : src.getClinicalTrial()) tgt.addClinicalTrial(convertReference(t)); + for (org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameComponent srcName : src.getName()) { + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameComponent tgtName = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameComponent(); + copyElement(srcName, tgtName); + tgt.addName(tgtName); + tgtName.setProductNameElement(convertString(srcName.getProductNameElement())); + //type (new) + for (org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameNamePartComponent srcPart : srcName.getNamePart()) { + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameNamePartComponent tgtPart = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameNamePartComponent(); + copyElement(srcPart, tgtPart); + tgtName.addNamePart(tgtPart); + tgtPart.setPartElement(convertString(srcPart.getPartElement())); + tgtPart.setType(convertCoding(srcPart.getType())); + } + for (org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameCountryLanguageComponent srcLang : srcName.getCountryLanguage()) { + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameCountryLanguageComponent tgtLang = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameCountryLanguageComponent(); + copyElement(srcLang, tgtLang); + tgtName.addCountryLanguage(tgtLang); + tgtLang.setCountry(convertCodeableConcept(srcLang.getCountry())); + tgtLang.setLanguage(convertCodeableConcept(srcLang.getLanguage())); + } + } + for (org.hl7.fhir.r4.model.Identifier t : src.getCrossReference()) { + // cross-reference Identifier -> complex + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionCrossReferenceComponent c = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionCrossReferenceComponent(); + tgt.addCrossReference(c); + c.setProduct(convertIdentifier(t)); + } + for (org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductManufacturingBusinessOperationComponent srcMBO : src.getManufacturingBusinessOperation()) { + org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionManufacturingBusinessOperationComponent tgtMBO = new org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionManufacturingBusinessOperationComponent(); + copyElement(srcMBO, tgtMBO); + tgt.addManufacturingBusinessOperation(tgtMBO); + if (srcMBO.hasOperationType()) { + tgtMBO.setType(convertCodeableConcept(srcMBO.getOperationType())); + // operationType -> type[x] + } + if (srcMBO.hasAuthorisationReferenceNumber()) + throw new FHIRException("Converting MedicinalProduct.ManufacturingBusinessOperation.authorizationReferenceNumber is not supported"); + if (srcMBO.hasEffectiveDate()) { + // effectiveDate - dateTime -> Period + org.hl7.fhir.r5.model.Period p = new org.hl7.fhir.r5.model.Period(); + tgtMBO.setEffectiveDate(p); + p.setStartElement(convertDateTime(srcMBO.getEffectiveDateElement())); + p.setEndElement(convertDateTime(srcMBO.getEffectiveDateElement())); + } + if (srcMBO.hasConfidentialityIndicator()) + tgtMBO.setConfidentialityIndicator(convertCodeableConcept(srcMBO.getConfidentialityIndicator())); + for (org.hl7.fhir.r4.model.Reference t : srcMBO.getManufacturer()) tgtMBO.addManufacturer(convertReference(t)); + if (srcMBO.hasRegulator()) + throw new FHIRException("Converting MedicinalProduct.ManufacturingBusinessOperation.regulator is not supported"); + // added authorization + } + if (src.hasSpecialDesignation()) + throw new FHIRException("Converting MedicinalProduct.specialDesignation is not supported"); + + return tgt; + } +// Todo convert references + + public static org.hl7.fhir.r4.model.MedicinalProduct convertMedicinalProductDefinition(org.hl7.fhir.r5.model.MedicinalProductDefinition src) throws FHIRException { + if (src == null) + return null; + org.hl7.fhir.r4.model.MedicinalProduct tgt = new org.hl7.fhir.r4.model.MedicinalProduct(); + copyDomainResource(src, tgt); + for (org.hl7.fhir.r5.model.Identifier t : src.getIdentifier()) tgt.addIdentifier(convertIdentifier(t)); + if (src.hasType()) + tgt.setType(convertCodeableConcept(src.getType())); + if (src.hasDomain()) + tgt.setDomain(convertCoding(src.getDomain())); + if (src.hasVersion()) + throw new FHIRException("Converting MedicinalProductDefinition.version is not supported"); + if (src.hasStatus()) + throw new FHIRException("Converting MedicinalProductDefinition.status is not supported"); + if (src.hasDescription()) + throw new FHIRException("Converting MedicinalProductDefinition.description is not supported"); + if (src.hasCombinedPharmaceuticalDoseForm()) + tgt.setCombinedPharmaceuticalDoseForm(convertCodeableConcept(src.getCombinedPharmaceuticalDoseForm())); + if (src.hasIndication()) + throw new FHIRException("Converting MedicinalProductDefinition.indication is not supported"); + if (src.hasLegalStatusOfSupply()) + tgt.setLegalStatusOfSupply(convertCodeableConcept(src.getLegalStatusOfSupply())); + if (src.hasAdditionalMonitoringIndicator()) + tgt.setAdditionalMonitoringIndicator(convertCodeableConcept(src.getAdditionalMonitoringIndicator())); + for (org.hl7.fhir.r5.model.CodeableConcept t : src.getSpecialMeasures()) { + // specialMeasures CodeableConcept -> string + if (t.hasText()) { + org.hl7.fhir.r4.model.StringType s = tgt.addSpecialMeasuresElement(); + copyElement(t.getTextElement(), s); + s.setValue(t.getText()); + } + checkBase(t, "MedicinalProductDefinition.specialMeasures"); + if (t.hasCoding()) + throw new FHIRException("Converting MedicinalProductDefinition.specialMeasures.coding is not supported"); + } + if (src.hasPaediatricUseIndicator()) + tgt.setPaediatricUseIndicator(convertCodeableConcept(src.getPaediatricUseIndicator())); + for (org.hl7.fhir.r5.model.CodeableConcept t : src.getProductClassification()) tgt.addProductClassification(convertCodeableConcept(t)); + for (org.hl7.fhir.r5.model.MarketingStatus t : src.getMarketingStatus()) tgt.addMarketingStatus(convertMarketingStatus(t)); + for (org.hl7.fhir.r5.model.Reference t : src.getPharmaceuticalProduct()) tgt.addPharmaceuticalProduct(convertReference(t)); + for (org.hl7.fhir.r5.model.Reference t : src.getPackagedMedicinalProduct()) tgt.addPackagedMedicinalProduct(convertReference(t)); + // ingredient (new) + for (org.hl7.fhir.r5.model.Reference t : src.getAttachedDocument()) tgt.addAttachedDocument(convertReference(t)); + for (org.hl7.fhir.r5.model.Reference t : src.getMasterFile()) tgt.addMasterFile(convertReference(t)); + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionContactComponent t : src.getContact()) { + // contact complex -> reference + if (t.hasContact()) { + tgt.addContact(convertReference(t.getContact())); + } + checkBase(t, "MedicinalProductDefinition.contact"); + if (t.hasType()) + throw new FHIRException("Converting MedicinalProductDefinition.contact.type is not supported"); + } + for (org.hl7.fhir.r5.model.Reference t : src.getClinicalTrial()) tgt.addClinicalTrial(convertReference(t)); + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameComponent srcName : src.getName()) { + org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameComponent tgtName = new org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameComponent(); + copyElement(srcName, tgtName); + tgt.addName(tgtName); + tgtName.setProductNameElement(convertString(srcName.getProductNameElement())); + //type (new) + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameNamePartComponent srcPart : srcName.getNamePart()) { + org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameNamePartComponent tgtPart = new org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameNamePartComponent(); + copyElement(srcPart, tgtPart); + tgtName.addNamePart(tgtPart); + tgtPart.setPartElement(convertString(srcPart.getPartElement())); + tgtPart.setType(convertCoding(srcPart.getType())); + } + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionNameCountryLanguageComponent srcLang : srcName.getCountryLanguage()) { + org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameCountryLanguageComponent tgtLang = new org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductNameCountryLanguageComponent(); + copyElement(srcLang, tgtLang); + tgtName.addCountryLanguage(tgtLang); + tgtLang.setCountry(convertCodeableConcept(srcLang.getCountry())); + tgtLang.setLanguage(convertCodeableConcept(srcLang.getLanguage())); + } + } + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionCrossReferenceComponent t : src.getCrossReference()) { + // cross-reference complex -> Identifier + if (t.hasProduct()) { + if (t.getProduct() instanceof org.hl7.fhir.r5.model.Identifier) + tgt.addCrossReference(convertIdentifier((org.hl7.fhir.r5.model.Identifier)t.getProduct())); + else + throw new FHIRException("Converting MedicinalProductDefinition.crossReference.productReference is not supported"); + } + checkBase(t, "MedicinalProductDefinition.crossReference"); + if (t.hasType()) + throw new FHIRException("Converting MedicinalProductDefinition.crossReference.type is not supported"); + } + for (org.hl7.fhir.r5.model.MedicinalProductDefinition.MedicinalProductDefinitionManufacturingBusinessOperationComponent srcMBO : src.getManufacturingBusinessOperation()) { + org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductManufacturingBusinessOperationComponent tgtMBO = new org.hl7.fhir.r4.model.MedicinalProduct.MedicinalProductManufacturingBusinessOperationComponent(); + copyElement(srcMBO, tgtMBO); + tgt.addManufacturingBusinessOperation(tgtMBO); + if (srcMBO.hasType()) { + // type[x] -> operationType + if (srcMBO.getType() instanceof org.hl7.fhir.r5.model.CodeableConcept) + tgtMBO.setOperationType(convertCodeableConcept((org.hl7.fhir.r5.model.CodeableConcept)srcMBO.getType())); + else + throw new FHIRException("Converting MedicinalProductDefinition.manufacturingBusinessOperation.typeReference is not supported"); + } + // added authorisationReferenceNumber + if (srcMBO.hasEffectiveDate()) { + // effectiveDate - Period -> dateTime + org.hl7.fhir.r5.model.Period d = srcMBO.getEffectiveDate(); + checkBase(srcMBO, "MedicinalProductDefinition.manufacturingBusinessOperation.effectiveDate"); + if (d.hasStart() || d.hasEnd()) { + if (d.hasStart() != d.hasEnd() || !d.getStart().equals(d.getEnd())) + throw new FHIRException("Converting MedicinalProductDefinition.manufacturingBusinessOperation.effectiveDate is not supported when start is not identical to end"); + else + tgtMBO.setEffectiveDateElement(convertDateTime(d.getStartElement())); + } + } + if (srcMBO.hasAuthorization()) + throw new FHIRException("Converting MedicinalProductDefinition.manufacturingBusinessOperation.authorization is not supported"); + if (srcMBO.hasConfidentialityIndicator()) + tgtMBO.setConfidentialityIndicator(convertCodeableConcept(srcMBO.getConfidentialityIndicator())); + for (org.hl7.fhir.r5.model.Reference t : srcMBO.getManufacturer()) tgtMBO.addManufacturer(convertReference(t)); + // Added regulator + // added authorization + } + // added specialDesignation + return tgt; + } + + static public void checkBase(org.hl7.fhir.r5.model.Element e, String path) throws FHIRException { + if (e.hasId()) + throw new FHIRException ("Converting " + path + ".id is not supported"); + if (e.hasExtension()) + throw new FHIRException ("Converting " + path + ".extension is not supported"); + } + + static public org.hl7.fhir.r5.model.Enumeration convertMedicationStatementStatus(org.hl7.fhir.r4.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.r5.model.Enumeration tgt = new org.hl7.fhir.r5.model.Enumeration<>(new org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodesEnumFactory()); + VersionConvertor_40_50.copyElement(src, tgt); + switch(src.getValue()) { + case ACTIVE: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.ACTIVE); + break; + case COMPLETED: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.COMPLETED); + break; + case ENTEREDINERROR: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.ENTEREDINERROR); + break; + case INTENDED: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.INTENDED); + break; + case STOPPED: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.STOPPED); + break; + case ONHOLD: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.ONHOLD); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.UNKNOWN); + break; + case NOTTAKEN: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.NOTTAKEN); + break; + default: + tgt.setValue(org.hl7.fhir.r5.model.MedicationUsage.MedicationUsageStatusCodes.NULL); + break; + } + return tgt; + } + + static public org.hl7.fhir.r4.model.Enumeration convertMedicationStatementStatus(org.hl7.fhir.r5.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.r4.model.Enumeration tgt = new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatusEnumFactory()); + VersionConvertor_40_50.copyElement(src, tgt); + switch(src.getValue()) { + case ACTIVE: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.ACTIVE); + break; + case COMPLETED: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.COMPLETED); + break; + case ENTEREDINERROR: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.ENTEREDINERROR); + break; + case INTENDED: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.INTENDED); + break; + case STOPPED: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.STOPPED); + break; + case ONHOLD: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.ONHOLD); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.UNKNOWN); + break; + case NOTTAKEN: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.NOTTAKEN); + break; + default: + tgt.setValue(org.hl7.fhir.r4.model.MedicationStatement.MedicationStatementStatus.NULL); + break; + } + return tgt; + } +} From 071a3dd9271db4a5fa23e970817eb3a5dbffd7bd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 5 Mar 2020 16:47:28 +1100 Subject: [PATCH 45/52] Properly process invariants that have a source in the validator --- .../instance/InstanceValidator.java | 19 +++++++++++++++++-- .../validation/tests/ValidationTestSuite.java | 3 +++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index c85dcde3d..75b782929 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -4706,7 +4706,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } NodeStack localStack = stack.push(ei.getElement(), ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); if (debug) { - System.out.println(" " + localStack.getLiteralPath()); + System.out.println(" check " + localStack.getLiteralPath()+" against "+ei.getDefinition().getId()+" in profile "+profile.getUrl()); } String localStackLiterapPath = localStack.getLiteralPath(); String eiPath = ei.getPath(); @@ -5154,7 +5154,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return; for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || profile.getUrl().equals(inv.getSource()))) { + if (inv.hasExpression() && (!onlyNonInherited || !inv.hasSource() || !isInheritedProfile(profile, inv.getSource()))) { @SuppressWarnings("unchecked") Set invList = executionId.equals(element.getUserString(EXECUTION_ID)) ? (Set) element.getUserData(EXECUTED_CONSTRAINT_LIST) : null; if (invList == null) { @@ -5172,6 +5172,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + private boolean isInheritedProfile(StructureDefinition profile, String source) { + if (source.equals(profile.getUrl())) { + return false; + } + while (profile != null) { + profile = context.fetchResource(StructureDefinition.class, profile.getBaseDefinition()); + if (profile != null) { + if (source.equals(profile.getUrl())) { + return true; + } + } + } + return false; + } + public void checkInvariant(ValidatorHostContext hostContext, List errors, String path, StructureDefinition profile, Element resource, Element element, ElementDefinitionConstraintComponent inv) throws FHIRException { ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); if (n == null) { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 4006de2f9..48bfcbedc 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -191,6 +191,9 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour if (content.has("profile")) { System.out.print("** Profile: "); JsonObject profile = content.getAsJsonObject("profile"); + if (profile.getAsJsonObject("java").has("debug")) { + val.setDebug(profile.getAsJsonObject("java").get("debug").getAsBoolean()); + } if (profile.has("supporting")) { for (JsonElement e : profile.getAsJsonArray("supporting")) { String filename = e.getAsString(); From d8262c056c7b2c59c4fef410534961e061dc15fb Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 5 Mar 2020 16:54:07 +1100 Subject: [PATCH 46/52] Release new version 4.2.6 --- org.hl7.fhir.convertors/pom.xml | 2 +- org.hl7.fhir.dstu2/pom.xml | 2 +- org.hl7.fhir.dstu2016may/pom.xml | 2 +- org.hl7.fhir.dstu3/pom.xml | 2 +- org.hl7.fhir.r4/pom.xml | 2 +- org.hl7.fhir.r5/pom.xml | 2 +- org.hl7.fhir.utilities/pom.xml | 2 +- org.hl7.fhir.validation.cli/pom.xml | 2 +- org.hl7.fhir.validation/pom.xml | 2 +- pom.xml | 4 ++-- release.bat | 4 ++-- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index 16026130e..4f5b5e502 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 5211191cc..2cf2308da 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 06ce6ac48..a89a59b79 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index 11a5e2379..3c79ef345 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index 0d3df0d68..1748b7e13 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index b2a6107dd..8fbe61fd4 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 9e240001d..1d912c2a9 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 2a28ae91c..1f44225c9 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index f603b1f0a..5374a1975 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 90d080ae2..45fc01c59 100644 --- a/pom.xml +++ b/pom.xml @@ -13,11 +13,11 @@ each other. It is fine to bump the point version of this POM without affecting HAPI FHIR. --> - 4.2.5-SNAPSHOT + 4.2.6-SNAPSHOT 4.2.0 - 1.0.45-SNAPSHOT + 1.0.46-SNAPSHOT org.hl7.fhir.core diff --git a/release.bat b/release.bat index 60d869613..71d8b70b2 100644 --- a/release.bat +++ b/release.bat @@ -1,7 +1,7 @@ @echo off -set oldver=4.2.4 -set newver=4.2.5 +set oldver=4.2.5 +set newver=4.2.6 echo .. echo ========================================================================= From bb11af20fdfd52baa9ccc2d6f685e29838297996 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Thu, 5 Mar 2020 22:16:35 +0100 Subject: [PATCH 47/52] improved recursive if scanning ignores --- .../java/org/hl7/fhir/validation/ValidationEngine.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 69b434d3a..cb18d3eb7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -436,6 +436,8 @@ public class ValidationEngine implements IValidatorResourceFetcher { File f = new File(Utilities.path(src)); if (f.exists()) { + if(isIgnoreFile(f)) + return null; if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) return loadPackage(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz")); if (f.isDirectory() && new File(Utilities.path(src, "igpack.zip")).exists()) @@ -547,8 +549,10 @@ public class ValidationEngine implements IValidatorResourceFetcher { } private boolean isIgnoreFile(File ff) { - return Utilities.existsInList(ff.getName(), ".DS_Store") || Utilities.existsInList(Utilities.getFileExtension(ff.getName()).toLowerCase(), "md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"); - + if (ff.getName().startsWith(".")|| ff.getAbsolutePath().contains(".git")){ + return true; + } + return Utilities.existsInList(Utilities.getFileExtension(ff.getName()).toLowerCase(), "md", "css", "js", "png", "gif", "jpg", "html", "tgz", "pack", "zip"); } private Map loadPackage(InputStream stream, String name) throws Exception { From 7858ba9717db45c9df5d575aa82dfd0e6f0e8fbf Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Thu, 5 Mar 2020 22:19:19 +0100 Subject: [PATCH 48/52] removed unnecessary check --- .../src/main/java/org/hl7/fhir/validation/ValidationEngine.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index cb18d3eb7..5de2877c0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -436,8 +436,6 @@ public class ValidationEngine implements IValidatorResourceFetcher { File f = new File(Utilities.path(src)); if (f.exists()) { - if(isIgnoreFile(f)) - return null; if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) return loadPackage(new FileInputStream(Utilities.path(src, "package.tgz")), Utilities.path(src, "package.tgz")); if (f.isDirectory() && new File(Utilities.path(src, "igpack.zip")).exists()) From 2050052c956b99a8fdf3574072750dc8bcae9247 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:28:45 +1100 Subject: [PATCH 49/52] fix problem with bad maps from core spec --- .../fhir/r5/conformance/ProfileUtilities.java | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index a9135390a..a467929b7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -585,6 +585,13 @@ public class ProfileUtilities extends TranslatingUtilities { else messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR)); } + // hack around a problem in R4 definitions (somewhere?) + for (ElementDefinition ed : derived.getSnapshot().getElement()) { + for (ElementDefinitionMappingComponent mm : ed.getMapping()) { + mm.setMap(mm.getMap().trim()); + + } + } if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition ed : derived.getSnapshot().getElement()) { if (!ed.hasBase()) { @@ -2490,15 +2497,18 @@ public class ProfileUtilities extends TranslatingUtilities { for (ElementDefinitionMappingComponent d : base.getMapping()) { found = found || (d.getIdentity().equals(s.getIdentity()) && d.getMap().equals(s.getMap())); } - if (!found) + if (!found) { base.getMapping().add(s); + } } } - else if (trimDifferential) + else if (trimDifferential) { derived.getMapping().clear(); - else - for (ElementDefinitionMappingComponent t : derived.getMapping()) + } else { + for (ElementDefinitionMappingComponent t : derived.getMapping()) { t.setUserData(DERIVATION_EQUALS, true); + } + } } for (ElementDefinitionMappingComponent m : base.getMapping()) { if (m.hasMap()) { @@ -2509,8 +2519,9 @@ public class ProfileUtilities extends TranslatingUtilities { // todo: constraints are cumulative. there is no replacing for (ElementDefinitionConstraintComponent s : base.getConstraint()) { s.setUserData(IS_DERIVED, true); - if (!s.hasSource()) + if (!s.hasSource()) { s.setSource(srcSD.getUrl()); + } } if (derived.hasConstraint()) { for (ElementDefinitionConstraintComponent s : derived.getConstraint()) { @@ -2521,19 +2532,22 @@ public class ProfileUtilities extends TranslatingUtilities { } } for (IdType id : derived.getCondition()) { - if (!base.hasCondition(id)) + if (!base.hasCondition(id)) { base.getCondition().add(id); + } } // now, check that we still have a bindable type; if not, delete the binding - see task 8477 - if (dest.hasBinding() && !hasBindableType(dest)) + if (dest.hasBinding() && !hasBindableType(dest)) { dest.setBinding(null); + } // finally, we copy any extensions from source to dest for (Extension ex : derived.getExtension()) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, ex.getUrl()); - if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) + if (sd == null || sd.getSnapshot() == null || sd.getSnapshot().getElementFirstRep().getMax().equals("1")) { ToolingExtensions.removeExtension(dest, ex.getUrl()); + } dest.addExtension(ex.copy()); } } @@ -2622,18 +2636,21 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean hasBindableType(ElementDefinition ed) { for (TypeRefComponent tr : ed.getType()) { - if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code")) + if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code")) { return true; + } } return false; } private boolean isLargerMax(String derived, String base) { - if ("*".equals(base)) + if ("*".equals(base)) { return false; - if ("*".equals(derived)) + } + if ("*".equals(derived)) { return true; + } return Integer.parseInt(derived) > Integer.parseInt(base); } @@ -2645,10 +2662,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean codesInExpansion(List contains, ValueSetExpansionComponent expansion) { for (ValueSetExpansionContainsComponent cc : contains) { - if (!inExpansion(cc, expansion.getContains())) + if (!inExpansion(cc, expansion.getContains())) { return false; - if (!codesInExpansion(cc.getContains(), expansion)) + } + if (!codesInExpansion(cc.getContains(), expansion)) { return false; + } } return true; } @@ -2656,10 +2675,12 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean inExpansion(ValueSetExpansionContainsComponent cc, List contains) { for (ValueSetExpansionContainsComponent cc1 : contains) { - if (cc.getSystem().equals(cc1.getSystem()) && cc.getCode().equals(cc1.getCode())) + if (cc.getSystem().equals(cc1.getSystem()) && cc.getCode().equals(cc1.getCode())) { return true; - if (inExpansion(cc, cc1.getContains())) + } + if (inExpansion(cc, cc1.getContains())) { return true; + } } return false; } @@ -2707,24 +2728,28 @@ public class ProfileUtilities extends TranslatingUtilities { private int findEnd(List list, ElementDefinition ed, int cursor) { String path = ed.getPath()+"."; - while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) + while (cursor < list.size() && list.get(cursor).getPath().startsWith(path)) { cursor++; + } return cursor; } private ElementDefinition getMatchInDerived(ElementDefinition ed, List list) { - for (ElementDefinition t : list) - if (t.getPath().equals(ed.getPath())) + for (ElementDefinition t : list) { + if (t.getPath().equals(ed.getPath())) { return t; + } + } return null; } private ElementDefinition getMatchInDerived(ElementDefinition ed, List list, int start, int end) { for (int i = start; i < end; i++) { ElementDefinition t = list.get(i); - if (t.getPath().equals(ed.getPath())) + if (t.getPath().equals(ed.getPath())) { return t; + } } return null; } @@ -2732,8 +2757,9 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean isImmediateChild(ElementDefinition ed) { String p = ed.getPath(); - if (!p.contains(".")) + if (!p.contains(".")) { return false; + } p = p.substring(p.indexOf(".")+1); return !p.contains("."); } From 9bbdb8d9b1cbde10b194190c20c1a817fd88e222 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:30:13 +1100 Subject: [PATCH 50/52] Improvements to ValueSet definition rendering --- .../hl7/fhir/r5/utils/NarrativeGenerator.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index cb3c3b669..ae675e911 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -3476,10 +3476,10 @@ public class NarrativeGenerator implements INarrativeGenerator { return false; } - private void generateCopyright(XhtmlNode x, ValueSet vs) { + private void generateCopyright(XhtmlNode x, ValueSet vs) throws FHIRFormatError, DefinitionException, IOException { XhtmlNode p = x.para(); p.b().tx("Copyright Statement:"); - smartAddText(p, " " + vs.getCopyright()); + addMarkdown(x, vs.getCopyright()); } private XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, String lang, List properties) { @@ -3887,18 +3887,22 @@ public class NarrativeGenerator implements INarrativeGenerator { if (vs.hasCopyrightElement()) generateCopyright(x, vs); } - XhtmlNode p = x.para(); - p.tx("This value set includes codes from the following code systems:"); - XhtmlNode ul = x.ul(); - XhtmlNode li; - for (ConceptSetComponent inc : vs.getCompose().getInclude()) { - hasExtensions = genInclude(rcontext, ul, inc, "Include", langs, maps) || hasExtensions; - } - for (ConceptSetComponent exc : vs.getCompose().getExclude()) { - hasExtensions = genInclude(rcontext, ul, exc, "Exclude", langs, maps) || hasExtensions; - } + if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) { + hasExtensions = genInclude(rcontext, ul, vs.getCompose().getInclude().get(0), "Include", langs, maps) || hasExtensions; + } else { + XhtmlNode p = x.para(); + p.tx("This value set includes codes based on the following rules:"); + XhtmlNode li; + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + hasExtensions = genInclude(rcontext, ul, inc, "Include", langs, maps) || hasExtensions; + } + for (ConceptSetComponent exc : vs.getCompose().getExclude()) { + hasExtensions = genInclude(rcontext, ul, exc, "Exclude", langs, maps) || hasExtensions; + } + } + // now, build observed languages if (langs.size() > 0) { @@ -4000,6 +4004,10 @@ public class NarrativeGenerator implements INarrativeGenerator { if (inc.getConcept().size() > 0) { li.addText(type+" these codes as defined in "); addCsRef(inc, li, e); + if (inc.hasVersion()) { + li.addText(" version "); + li.code(inc.getVersion()); + } XhtmlNode t = li.table("none"); boolean hasComments = false; From dcb3e1f7f0865a57113c5bac03cf3fb48321a22a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 12:30:41 +1100 Subject: [PATCH 51/52] improve documentation --- .../org/hl7/fhir/validation/ValidationEngine.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 4fda919e7..4e0f3b622 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -106,19 +106,15 @@ POSSIBILITY OF SUCH DAMAGE. * * 1/ Initialize * ValidationEngine validator = new ValidationEngine(src); - * - this must refer to the igpack.zip for the version of the spec against which you want to validate - * it can be a url or a file reference. It can nominate the igpack.zip directly, - * or it can name the container alone (e.g. just the spec URL). - * The validation engine does not cache igpack.zip. the user must manage that if desired + * - this must be the packageId of the relevant core specification + * for the version you want to validate against (e.g. hl7.fhir.r4.core) * * validator.connectToTSServer(txServer); * - this is optional; in the absence of a terminology service, snomed, loinc etc will not be validated * * validator.loadIg(src); - * - call this any number of times for the Implementation Guide(s) of interest. This is a reference - * to the igpack.zip for the implementation guide - same rules as above - * the version of the IGPack must match that of the spec - * Alternatively it can point to a local folder that contains conformance resources. + * - call this any number of times for the Implementation Guide(s) of interest. + * - See https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator for documentation about the src parameter (-ig parameter) * * validator.loadQuestionnaire(src) * - url or filename of a questionnaire to load. Any loaded questionnaires will be used while validating @@ -136,7 +132,8 @@ POSSIBILITY OF SUCH DAMAGE. * if the source is provided as byte[] or stream, you need to provide a format too, though you can * leave that as null, and the validator will guess * - * 3. Or, instead of validating, transform + * 3. Or, instead of validating, transform (see documentation and use in Validator.java) + * * @author Grahame Grieve * */ From fe188da8bdc6df074b37551ec5cba14739af3e97 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 6 Mar 2020 16:14:00 +1100 Subject: [PATCH 52/52] fix rendering of slicing in profiles --- .../java/org/hl7/fhir/r5/conformance/ProfileUtilities.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index a467929b7..7813013a9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -3329,8 +3329,10 @@ public class ProfileUtilities extends TranslatingUtilities { s = "@"+s; String hint = ""; hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : "")); - hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); - hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); + if (hasDef && element.hasDefinition()) { + hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); + hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); + } Cell left = gen.new Cell(null, ref, s, hint, null); row.getCells().add(left); Cell gc = gen.new Cell();