fix to CDA xsi:type validation per SD decision + apply regex pattern to literal format if defined + improvements to vital signs related messages
This commit is contained in:
parent
2cfc7f3df7
commit
e73404fe8a
|
@ -103,6 +103,7 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.Constants;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.DataType;
|
||||
import org.hl7.fhir.r5.model.DateTimeType;
|
||||
|
@ -2932,9 +2933,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
String regext = FHIRPathExpressionFixer.fixRegex(getRegexFromType(e.fhirType()));
|
||||
if (regext != null) {
|
||||
try {
|
||||
boolean matches = e.primitiveValue().matches(regext);
|
||||
String pt = e.primitiveValue();
|
||||
String ptFmt = null;
|
||||
if (e.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_DATE_FORMAT)) {
|
||||
ptFmt = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(e.getProperty().getDefinition(), ToolingExtensions.EXT_DATE_FORMAT), pt);
|
||||
}
|
||||
boolean matches = pt.matches(regext) || (ptFmt != null && ptFmt.matches(regext));
|
||||
if (!matches) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE, e.primitiveValue(), e.fhirType(), regext) && ok;
|
||||
if (ptFmt == null) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE, pt, e.fhirType(), regext) && ok;
|
||||
} else {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, matches, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_TYPE_ALT, pt, ptFmt, e.fhirType(), regext) && ok;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_REGEX_EXCEPTION, regext, e.fhirType(), ex.getMessage()) && ok;
|
||||
|
@ -2946,6 +2956,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException {
|
||||
if ("v3".equals(fmt) || "YYYYMMDDHHMMSS.UUUU[+|-ZZzz]".equals(fmt)) {
|
||||
DateTimeType d = new DateTimeType(av);
|
||||
return d.getAsV3();
|
||||
} else
|
||||
throw new FHIRException(context.formatMessage(I18nConstants.UNKNOWN_DATE_FORMAT_, fmt));
|
||||
}
|
||||
|
||||
private boolean isCoreDefinition(StructureDefinition profile) {
|
||||
return profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && profile.getKind() != StructureDefinitionKind.LOGICAL;
|
||||
}
|
||||
|
@ -4289,7 +4307,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (sd != null && (sd.getTypeTail().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) {
|
||||
return sd;
|
||||
}
|
||||
if (sd != null && sd.getAbstract()) {
|
||||
if (sd != null && (sd.getAbstract() || sd.getUrl().startsWith(Constants.NS_CDA_ROOT))) {
|
||||
StructureDefinition sdt = context.fetchTypeDefinition(type);
|
||||
StructureDefinition tt = sdt;
|
||||
while (tt != null) {
|
||||
|
@ -4687,10 +4705,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
timeTracker.sd(t);
|
||||
if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot())
|
||||
return sd.getSnapshot().getElement().get(0);
|
||||
if (sd != null) {
|
||||
StructureDefinition sdt = context.fetchTypeDefinition(type);
|
||||
if (inheritsFrom(sdt, sd) && sdt.hasSnapshot()) {
|
||||
return sdt.getSnapshot().getElement().get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean inheritsFrom(StructureDefinition sdt, StructureDefinition sd) {
|
||||
while (sdt != null) {
|
||||
if (sdt == sd) {
|
||||
return true;
|
||||
}
|
||||
sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
|
||||
this.anyExtensionsAllowed = anyExtensionsAllowed;
|
||||
}
|
||||
|
@ -6024,8 +6058,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
checkMustSupport(profile, ei);
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
if (checkDefn.getType().size() == 1 && !"*".equals(checkDefn.getType().get(0).getWorkingCode()) && !"Element".equals(checkDefn.getType().get(0).getWorkingCode())
|
||||
&& !"BackboneElement".equals(checkDefn.getType().get(0).getWorkingCode())) {
|
||||
boolean hasType = checkDefn.getType().size() > 0;
|
||||
boolean isAbstract = hasType && Utilities.existsInList(checkDefn.getType().get(0).getWorkingCode(), "Element", "BackboneElement");
|
||||
boolean isChoice = checkDefn.getType().size() > 1 || (hasType && "*".equals(checkDefn.getType().get(0).getWorkingCode()));
|
||||
boolean isCDAChoice = profile.getUrl().startsWith(Constants.NS_CDA_ROOT) && ei.getElement().getExplicitType() != null;
|
||||
|
||||
if (hasType && !isChoice && !isAbstract && !isCDAChoice) {
|
||||
type = checkDefn.getType().get(0).getWorkingCode();
|
||||
typeName = type;
|
||||
if (Utilities.isAbsoluteUrl(type)) {
|
||||
|
@ -6075,12 +6113,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
profiles.add(p.getValue());
|
||||
}
|
||||
}
|
||||
} else if (checkDefn.getType().size() > 1) {
|
||||
} else if (checkDefn.getType().size() > 1 || isCDAChoice) {
|
||||
|
||||
String prefix = tail(checkDefn.getPath());
|
||||
assert typesAreAllReference(checkDefn.getType()) || checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") || isResourceAndTypes(checkDefn) : "Multiple Types allowed, but name is wrong @ "+checkDefn.getPath()+": "+checkDefn.typeSummaryVB();
|
||||
assert typesAreAllReference(checkDefn.getType()) || isCDAChoice|| checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR) || prefix.endsWith("[x]") || isResourceAndTypes(checkDefn) : "Multiple Types allowed, but name is wrong @ "+checkDefn.getPath()+": "+checkDefn.typeSummaryVB();
|
||||
|
||||
if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
if (isCDAChoice) {
|
||||
type = ei.getElement().getExplicitType();
|
||||
typeName = type;
|
||||
} else if (checkDefn.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
|
||||
type = ei.getElement().getType();
|
||||
typeName = type;
|
||||
} else if (ei.getElement().isResource()) {
|
||||
|
@ -6476,7 +6517,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
hintPlural(errors, NO_RULE_DATE, IssueType.NOTSUPPORTED, element.line(), element.col(), stack.getLiteralPath(), count >= ed.getMin(), count, I18nConstants.VALIDATION_VAL_PROFILE_NOCHECKMIN, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin()));
|
||||
else {
|
||||
if (count < ed.getMin()) {
|
||||
ok = rulePlural(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, count, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin())) && ok;
|
||||
if (isObservationMagicValue(profile, ed)) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM_MAGIC, ed.getSliceName(), getFixedLOINCCode(ed, profile), profile.getVersionedUrl()) && ok;
|
||||
} else {
|
||||
ok = rulePlural(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), false, count, I18nConstants.VALIDATION_VAL_PROFILE_MINIMUM, profile.getVersionedUrl(), ed.getPath(), ed.getId(), ed.getSliceName(),ed.getLabel(), stack.getLiteralPath(), Integer.toString(ed.getMin())) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6492,6 +6537,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String getFixedLOINCCode(ElementDefinition ed, StructureDefinition profile) {
|
||||
if (ed.hasFixedCoding() && "http://loinc.org".equals(ed.getFixedCoding().getSystem())) {
|
||||
return ed.getFixedCoding().getCode();
|
||||
}
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(profile, ed);
|
||||
if (children != null) {
|
||||
for (ElementDefinition t : children.getList()) {
|
||||
if (t.getPath().endsWith(".code") && t.hasFixed()) {
|
||||
return t.getFixed().primitiveValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
|
||||
private boolean isObservationMagicValue(StructureDefinition profile, ElementDefinition ed) {
|
||||
if (profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && ed.hasSliceName() && ed.getPath().equals("Observation.code.coding")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<String> assignChildren(ValidationContext valContext, List<ValidationMessage> errors, StructureDefinition profile, Element resource,
|
||||
NodeStack stack, SourcedChildDefinitions childDefinitions, List<ElementInfo> children, BooleanHolder bh) throws DefinitionException {
|
||||
// 2. assign children to a definition
|
||||
|
@ -6545,12 +6612,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
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, NO_RULE_DATE, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, isProfile(slicer) || isCritical(ei.sliceInfo),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
|
||||
profile == null ? "" : "defined in the profile " + profile.getVersionedUrl()),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : context.formatMessage(I18nConstants.DEFINED_IN_THE_PROFILE) + " "+profile.getVersionedUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
|
||||
errorSummaryForSlicingAsText(ei.sliceInfo));
|
||||
ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) {
|
||||
if (!ignoreSlicingHint(ei.definition, profile)) {
|
||||
slicingHint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, isProfile(slicer) || isCritical(ei.sliceInfo),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
|
||||
profile == null ? "" : "defined in the profile " + profile.getVersionedUrl()),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : context.formatMessage(I18nConstants.DEFINED_IN_THE_PROFILE) + " "+profile.getVersionedUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
|
||||
errorSummaryForSlicingAsText(ei.sliceInfo));
|
||||
}
|
||||
} else if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.CLOSED)) {
|
||||
bh.see(rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), false, I18nConstants.VALIDATION_VAL_PROFILE_NOTSLICE, (profile == null ? "" : " defined in the profile " + profile.getVersionedUrl()), errorSummaryForSlicing(ei.sliceInfo)));
|
||||
}
|
||||
|
@ -6593,6 +6662,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
|
||||
private boolean ignoreSlicingHint(ElementDefinition definition, StructureDefinition profile) {
|
||||
if (profile.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") && "Observation.code.coding".equals(definition.getPath())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ElementInfo> listChildren(Element element, NodeStack stack) {
|
||||
// 1. List the children, and remember their exact path (convenience)
|
||||
List<ElementInfo> children = new ArrayList<ElementInfo>();
|
||||
|
|
Loading…
Reference in New Issue