more XML checking for CDA + control over date rules
This commit is contained in:
parent
04597eea27
commit
84e3bec25f
|
@ -232,7 +232,7 @@ public class XmlParser extends ParserBase {
|
|||
|
||||
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
|
||||
result.setPath(element.getLocalName());
|
||||
checkElement(errors, element, path, result.getProperty());
|
||||
checkElement(errors, element, path, result.getProperty(), false);
|
||||
result.markLocation(line(element, false), col(element, false));
|
||||
result.setType(element.getLocalName());
|
||||
parseChildren(errors, path, element, result);
|
||||
|
@ -274,7 +274,7 @@ public class XmlParser extends ParserBase {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void checkElement(List<ValidationMessage> errors, org.w3c.dom.Element element, String path, Property prop) throws FHIRFormatError {
|
||||
private void checkElement(List<ValidationMessage> errors, org.w3c.dom.Element element, String path, Property prop, boolean xsiTypeChecked) throws FHIRFormatError {
|
||||
if (policy == ValidationPolicy.EVERYTHING) {
|
||||
if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.ELEMENT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR);
|
||||
|
@ -283,8 +283,20 @@ public class XmlParser extends ParserBase {
|
|||
if (elementNs == null) {
|
||||
elementNs = "noNamespace";
|
||||
}
|
||||
if (!elementNs.equals(ns))
|
||||
if (!elementNs.equals(ns)) {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.WRONG_NAMESPACE__EXPECTED_, ns), IssueSeverity.ERROR);
|
||||
}
|
||||
if (!xsiTypeChecked) {
|
||||
String xsiType = element.getAttributeNS(FormatUtilities.NS_XSI, "type");
|
||||
if (!Utilities.noString(xsiType)) {
|
||||
String actualType = prop.getXmlTypeName();
|
||||
if (!xsiType.equals(actualType)) {
|
||||
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_WRONG, xsiType, actualType), IssueSeverity.ERROR);
|
||||
} else {
|
||||
logError(errors, "2023-10-12", line(element, false), col(element, false), path, IssueType.INVALID, context.formatMessage(I18nConstants.XSI_TYPE_UNNECESSARY), IssueSeverity.INFORMATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +305,7 @@ public class XmlParser extends ParserBase {
|
|||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
|
||||
result.setPath(base.getLocalName());
|
||||
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
|
||||
checkElement(errors, base, path, result.getProperty());
|
||||
checkElement(errors, base, path, result.getProperty(), false);
|
||||
result.setType(base.getLocalName());
|
||||
parseChildren(errors, path, base, result);
|
||||
result.numberChildren();
|
||||
|
@ -434,7 +446,7 @@ public class XmlParser extends ParserBase {
|
|||
} else {
|
||||
n.setPath(element.getPath()+"."+property.getName());
|
||||
}
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty());
|
||||
boolean xsiTypeChecked = false;
|
||||
boolean ok = true;
|
||||
if (property.isChoice()) {
|
||||
if (property.getDefinition().hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
|
@ -453,9 +465,11 @@ public class XmlParser extends ParserBase {
|
|||
n.setType(xsiType);
|
||||
n.setExplicitType(xsiType);
|
||||
}
|
||||
xsiTypeChecked = true;
|
||||
} else
|
||||
n.setType(n.getType());
|
||||
}
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty(), xsiTypeChecked);
|
||||
element.getChildren().add(n);
|
||||
if (ok) {
|
||||
if (property.isResource())
|
||||
|
@ -486,7 +500,7 @@ public class XmlParser extends ParserBase {
|
|||
Element n = new Element(name, property).markLocation(line(child, false), col(child, false)).setFormat(FhirFormat.XML);
|
||||
cgn.getChildren().add(n);
|
||||
n.setPath(element.getPath()+"."+property.getName());
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty());
|
||||
checkElement(errors, (org.w3c.dom.Element) child, npath, n.getProperty(), false);
|
||||
parseChildren(errors, npath, (org.w3c.dom.Element) child, n);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1007,7 +1007,8 @@ public class I18nConstants {
|
|||
public static final String FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER = "FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER";
|
||||
public static final String FHIRPATH_NOT_A_COLLECTION = "FHIRPATH_NOT_A_COLLECTION";
|
||||
public static final String TERMINOLOGY_TX_UNKNOWN_OID = "TERMINOLOGY_TX_UNKNOWN_OID";
|
||||
public static final String TERMINOLOGY_TX_NO_SYSTEM = "TERMINOLOGY_TX_NO_SYSTEM";
|
||||
public static final String XSI_TYPE_WRONG = "XSI_TYPE_WRONG";
|
||||
public static final String XSI_TYPE_UNNECESSARY = "XSI_TYPE_UNNECESSARY";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ public class JsonObject extends JsonElement {
|
|||
}
|
||||
|
||||
public JsonObject add(String name, JsonElement value) throws JsonException {
|
||||
check(name != null, "Name is null");
|
||||
check(value != null, "Value is null");
|
||||
check(name != null, "Json Property Name is null");
|
||||
check(value != null, "Json Property Value is null");
|
||||
if (get(name) != null) {
|
||||
check(false, "Name '"+name+"' already exists (value = "+get(name).toString()+")");
|
||||
}
|
||||
|
|
|
@ -501,7 +501,6 @@ MEASURE_MR_GRP_MISSING_BY_CODE = The MeasureReport does not include a group for
|
|||
MEASURE_MR_GRP_NO_USABLE_CODE = None of the codes provided are usable for comparison - need both system and code on at least one code
|
||||
MEASURE_MR_GRP_NO_WRONG_CODE = The code provided ({0}) does not match the code specified in the measure report ({1})
|
||||
DUPLICATE_ID = Duplicate id value ''{0}''
|
||||
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning. A system should be provided
|
||||
MEASURE_MR_GRP_POP_NO_CODE = Group should have a code that matches the group population definition in the measure
|
||||
MEASURE_MR_GRP_POP_UNK_CODE = The code for this group population has no match in the measure definition
|
||||
MEASURE_MR_GRPST_POP_UNK_CODE = The code for this group stratifier has no match in the measure definition
|
||||
|
@ -1064,5 +1063,6 @@ FHIRPATH_CHOICE_NO_TYPE_SPECIFIER = The expression ''{0}'' refers to an element
|
|||
FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER = The expression ''{0}'' refers to an element that is not a choice, but has an .ofType(). SQL view runners are likely to pre-determine an incorrect full element name
|
||||
FHIRPATH_NOT_A_COLLECTION = Found a use of a collection operator on something that is not a collection at ''{0}'' - check that there's no mistakes in the expression syntax
|
||||
TERMINOLOGY_TX_UNKNOWN_OID = The OID ''{0}'' is not known
|
||||
TERMINOLOGY_TX_NO_SYSTEM = In the absence of a code system, the code cannot be validated
|
||||
|
||||
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning, and it cannot be validated. A system should be provided
|
||||
XSI_TYPE_WRONG = The xsi:type value ''{0}'' is wrong (should be ''{1}''). Note that xsi:type is unnecessary at this point
|
||||
XSI_TYPE_UNNECESSARY = xsi:type is unnecessary at this point
|
||||
|
|
|
@ -1660,7 +1660,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
c.setSystem(url);
|
||||
}
|
||||
} else {
|
||||
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NO_SYSTEM);
|
||||
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
|
||||
}
|
||||
|
||||
c.setCode(element.getNamedChildValue("code"));
|
||||
|
@ -1837,7 +1837,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
String code = element.getNamedChildValue("code");
|
||||
String system = element.getNamedChildValue("system");
|
||||
if (code != null && system == null) {
|
||||
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NO_SYSTEM);
|
||||
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
|
||||
}
|
||||
String version = element.getNamedChildValue("version");
|
||||
String display = element.getNamedChildValue("display");
|
||||
|
@ -1849,7 +1849,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
boolean ok = true;
|
||||
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, theSystem == null || isCodeSystemReferenceValid(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE) && ok;
|
||||
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, Utilities.noString(theCode) || !Utilities.noString(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
|
||||
|
||||
if (theSystem != null && theCode != null && !noTerminologyChecks) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, theSystem) && ok;
|
||||
|
@ -2625,12 +2624,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
boolean dok = ok = rule(errors, NO_RULE_DATE, 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, "'"+e.primitiveValue()+"' doesn't meet format requirements for dateTime") && ok;
|
||||
dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ) && dok;
|
||||
if (isCoreDefinition(profile) || (context.hasExtension(ToolingExtensions.EXT_DATE_RULES) && ToolingExtensions.readStringExtension(context, ToolingExtensions.EXT_DATE_RULES).contains("tz-for-time"))) {
|
||||
dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ) && dok;
|
||||
}
|
||||
dok = rule(errors, NO_RULE_DATE, 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()) && dok;
|
||||
if (dok) {
|
||||
try {
|
||||
DateTimeType dt = new DateTimeType(e.primitiveValue());
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
if (isCoreDefinition(profile) || (context.hasExtension(ToolingExtensions.EXT_DATE_RULES) && ToolingExtensions.readStringExtension(context, ToolingExtensions.EXT_DATE_RULES).contains("year-valid"))) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, ex.getMessage());
|
||||
dok = false;
|
||||
|
@ -2655,7 +2658,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (dok) {
|
||||
try {
|
||||
DateType dt = new DateType(e.primitiveValue());
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
if (isCoreDefinition(profile) || (context.hasExtension(ToolingExtensions.EXT_DATE_RULES) && ToolingExtensions.readStringExtension(context, ToolingExtensions.EXT_DATE_RULES).contains("year-valid"))) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID, ex.getMessage());
|
||||
dok = false;
|
||||
|
@ -2750,7 +2755,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (dok) {
|
||||
try {
|
||||
InstantType dt = new InstantType(e.primitiveValue());
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
if (isCoreDefinition(profile) || (context.hasExtension(ToolingExtensions.EXT_DATE_RULES) && ToolingExtensions.readStringExtension(context, ToolingExtensions.EXT_DATE_RULES).contains("year-valid"))) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID, ex.getMessage());
|
||||
dok = false;
|
||||
|
@ -2834,6 +2841,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean isCoreDefinition(StructureDefinition profile) {
|
||||
return profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && profile.getKind() != StructureDefinitionKind.LOGICAL;
|
||||
}
|
||||
|
||||
private String getRegexFromType(String fhirType) {
|
||||
StructureDefinition sd = context.fetchTypeDefinition(fhirType);
|
||||
if (sd != null) {
|
||||
|
@ -6149,6 +6160,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
private boolean checkCDACoding(List<ValidationMessage> errors, String path, boolean isPQ, Element element, StructureDefinition profile, ElementDefinition checkDefn, NodeStack stack, StructureDefinition defn, boolean inCodeableConcept, boolean checkDisplay) {
|
||||
boolean ok = true;
|
||||
String system = null;
|
||||
String code = element.getNamedChildValue(isPQ ? "unit" : "code");
|
||||
String oid = element.getNamedChildValue("codeSystem");
|
||||
if (oid != null) {
|
||||
String url = context.urlForOid(true, oid);
|
||||
|
@ -6159,9 +6171,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else {
|
||||
system = url;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, code == null, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
|
||||
}
|
||||
|
||||
String code = element.getNamedChildValue(isPQ ? "unit" : "code");
|
||||
String version = element.getNamedChildValue("codeSystemVersion");
|
||||
String display = element.getNamedChildValue("displayName");
|
||||
return checkCodedElement(errors, path, element, profile, checkDefn, inCodeableConcept, checkDisplay, stack, code, system, version, display) && ok;
|
||||
|
|
|
@ -1407,6 +1407,45 @@ v: {
|
|||
"code" : "CHE",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "en-US"
|
||||
}, "url": "http://terminology.hl7.org/ValueSet/v3-HumanLanguage", "version": "2.0.0", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"severity" : "error",
|
||||
"error" : "The CodeSystem http://terminology.hl7.org/CodeSystem/ietf3066 is unknown; Unable to check whether the code is in the value set http://terminology.hl7.org/ValueSet/v3-HumanLanguage|2.0.0 (from Tx-Server)",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"unknown-systems" : "http://terminology.hl7.org/CodeSystem/ietf3066",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "PRN"
|
||||
}, "valueSet" :null, "langs":"en", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"severity" : "error",
|
||||
"error" : "The CodeSystem is unknown (from Tx-Server)",
|
||||
"class" : "UNKNOWN",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
|||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "urn:oid:2.16.840.1.113883.5.10588",
|
||||
"code" : "GIM",
|
||||
"display" : "General internal medicine clinic"
|
||||
}, "valueSet" :null, "langs":"en", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"severity" : "error",
|
||||
"error" : "The CodeSystem urn:oid:2.16.840.1.113883.5.10588 is unknown (from Tx-Server)",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"unknown-systems" : "urn:oid:2.16.840.1.113883.5.10588",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
Loading…
Reference in New Issue