various improvements to structure map validation

This commit is contained in:
Grahame Grieve 2024-04-03 20:14:27 +11:00
parent 364a2aeec4
commit 808d4619d6
2 changed files with 45 additions and 16 deletions

View File

@ -195,6 +195,7 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.validation.IDigitalSignatureServices;
@ -3810,7 +3811,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
size = cnt.length;
}
} else if (url.startsWith("file:")) {
size = new File(url.substring(5)).length();
size = ManagedFileAccess.file(url.substring(5)).length();
} else {
fetchError = context.formatMessage(I18nConstants.TYPE_SPECIFIC_CHECKS_DT_ATT_UNKNOWN_URL_SCHEME, url); }
} catch (Exception e) {
@ -5699,7 +5700,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (element.getType().equals("StructureDefinition")) {
return new StructureDefinitionValidator(this, fpe, wantCheckSnapshotUnchanged).validateStructureDefinition(errors, element, stack) && ok;
} else if (element.getType().equals("StructureMap")) {
return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(errors, element, stack) && ok;
return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(valContext, errors, element, stack) && ok;
} else if (element.getType().equals("ValueSet")) {
return new ValueSetValidator(this).validateValueSet(valContext, errors, element, stack) && ok;
} else if ("http://hl7.org/fhir/uv/sql-on-fhir/StructureDefinition/ViewDefinition".equals(element.getProperty().getStructure().getUrl())) {

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.validation.instance.type;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -30,6 +31,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.structuremap.ResolvedGroup;
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
@ -38,6 +40,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidationContext;
public class StructureMapValidator extends BaseValidator {
@ -198,6 +201,20 @@ public class StructureMapValidator extends BaseValidator {
return summary();
}
public String getVersion() {
return VersionUtilities.getNameForVersion(sd.getFhirVersion().toCode());
}
public String sdSummary() {
if (sd == null) {
return "null";
} else if (!sd.hasUrl()) {
return "??";
} else {
return sd.getVersionedUrl();
}
}
}
public class VariableSet {
@ -313,7 +330,7 @@ public class StructureMapValidator extends BaseValidator {
}
public boolean validateStructureMap(List<ValidationMessage> errors, Element src, NodeStack stack) {
public boolean validateStructureMap(ValidationContext valContext, List<ValidationMessage> errors, Element src, NodeStack stack) {
boolean ok = true;
List<Element> imports = src.getChildrenByName("import");
int cc = 0;
@ -334,7 +351,7 @@ public class StructureMapValidator extends BaseValidator {
if (hasInputTypes(group) || group.hasUserData("structuremap.parameters")) {
group.setUserData("structuremap.validated", true);
fired = true;
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
ok = validateGroup(valContext, errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
}
}
cc++;
@ -345,7 +362,7 @@ public class StructureMapValidator extends BaseValidator {
for (Element group : groups) {
if (!group.hasUserData("structuremap.validated")) {
hint(errors, "2023-03-01", IssueType.INFORMATIONAL, group.line(), group.col(), stack.push(group, cc, null, null).getLiteralPath(), ok, I18nConstants.SM_ORPHAN_GROUP, group.getChildValue("name"));
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
ok = validateGroup(valContext, errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
}
cc++;
}
@ -378,7 +395,7 @@ public class StructureMapValidator extends BaseValidator {
return true;
}
private boolean validateGroup(List<ValidationMessage> errors, Element src, Element group, NodeStack stack, List<String> grpNames) {
private boolean validateGroup(ValidationContext valContext, List<ValidationMessage> errors, Element src, Element group, NodeStack stack, List<String> grpNames) {
String name = group.getChildValue("name");
boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, group.line(), group.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name);
if (!rule(errors, "2023-03-01", IssueType.INVALID, group.line(), group.col(), stack.getLiteralPath(), !grpNames.contains(name), I18nConstants.SM_GROUP_NAME_DUPLICATE, name)) {
@ -403,7 +420,7 @@ public class StructureMapValidator extends BaseValidator {
List<Element> structures = src.getChildrenByName("structure");
int cc = 0;
for (Element input : inputs) {
ok = validateInput(errors, src, group, input, stack.push(input, cc, null, null), structures, variables, pvars) && ok;
ok = validateInput(valContext, errors, src, group, input, stack.push(input, cc, null, null), structures, variables, pvars) && ok;
cc++;
}
@ -457,7 +474,7 @@ public class StructureMapValidator extends BaseValidator {
return grp;
}
private boolean validateInput(List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables, VariableSet pvars) {
private boolean validateInput(ValidationContext valContext, List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables, VariableSet pvars) {
boolean ok = false;
String gname = group.getChildValue("name");
String name = input.getChildValue("name");
@ -493,8 +510,15 @@ public class StructureMapValidator extends BaseValidator {
smode = structure.getChildValue("mode");
String url = structure.getChildValue("url");
sd = context.fetchResource(StructureDefinition.class, url);
if (sd == null) {
try {
sd = (StructureDefinition) fetcher.fetchCanonicalResource((IResourceValidator) parent, valContext.getAppContext(), url);
} catch (Exception e) {
// nothing?
}
}
if (sd == null) {
rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE, type, url);
warning(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE, type, url);
}
} else if (type != null) {
sd = context.fetchTypeDefinition(type);
@ -895,7 +919,7 @@ public class StructureMapValidator extends BaseValidator {
if (srcED != null) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcED.getBinding().hasValueSet() && srcED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_SOURCE)) {
srcVS = context.findTxResource(ValueSet.class, srcED.getBinding().getValueSet());
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcVS != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_SOURCE)) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcVS != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_SOURCE, srcED.getBinding().getValueSet())) {
ValueSetExpansionOutcome vse = context.expandVS(srcVS, true, false);
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE, vse.getError())) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
@ -914,7 +938,7 @@ public class StructureMapValidator extends BaseValidator {
if (srcED != null) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, tgtED.getBinding().hasValueSet() && tgtED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_TARGET)) {
ValueSet vs = context.findTxResource(ValueSet.class, tgtED.getBinding().getValueSet());
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vs != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_TARGET)) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vs != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_TARGET, tgtED.getBinding().getValueSet())) {
ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_TARGET, vse.getError())) {
List<String> systems = new ArrayList<>();
@ -1065,6 +1089,9 @@ public class StructureMapValidator extends BaseValidator {
throw new Error("Unable to resolve "+url);
} else {
ElementDefinition t2 = sdt.getSnapshot().getElementByPath(path);
if (t2 == null) {
t2 = sdt.getSnapshot().getElementById(path);
}
if (t2 == null) {
throw new Error("Unable to resolve "+path+" in "+url);
} else {
@ -1128,8 +1155,9 @@ public class StructureMapValidator extends BaseValidator {
String tgtType = tgtVar.getWorkingType();
if (rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), srcType != null, I18nConstants.SM_SOURCE_TYPE_NOT_FOUND) &&
rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), tgtType != null, I18nConstants.SM_TARGET_TYPE_NOT_FOUND)) {
StructureMapGroupComponent grp = findDefaultGroup(src, srcType, tgtType);
ok = rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), grp != null, I18nConstants.SM_MATCHING_RULEGROUP_NOT_FOUND, srcType, tgtType) && ok;
StructureMapGroupComponent grp = findDefaultGroup(src, srcType, srcVar.sd.getUrl(), tgtType, tgtVar.sd.getUrl());
ok = rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), grp != null, I18nConstants.SM_MATCHING_RULEGROUP_NOT_FOUND,
srcType+" ("+srcVar.sdSummary()+")", tgtType+" ("+tgtVar.sdSummary()+")") && ok;
} else {
ok = false;
}
@ -1213,7 +1241,7 @@ public class StructureMapValidator extends BaseValidator {
return type;
}
private StructureMapGroupComponent findDefaultGroup(Element src, String srcType, String tgtType) {
private StructureMapGroupComponent findDefaultGroup(Element src, String srcType, String srcUrl, String tgtType, String tgtUrl) {
List<Element> groups = src.getChildrenByName("group");
for (Element group : groups) {
if (Utilities.existsInList(group.getChildValue("typeMode"), "types", "type-and-types")) {
@ -1221,7 +1249,7 @@ public class StructureMapValidator extends BaseValidator {
if (inputs.size() == 2 && "source".equals(inputs.get(0).getChildValue("mode")) && "source".equals(inputs.get(0).getChildValue("mode"))) {
String srcT = resolveInputType(src, inputs.get(0));
String tgtT = resolveInputType(src, inputs.get(1));
if (sameTypes(srcT, srcType) && sameTypes(tgtT, tgtType)) {
if (sameTypes(srcT, srcType) && sameTypes(tgtT, tgtType) || sameTypes(srcT, srcUrl) && sameTypes(tgtT, tgtUrl)) {
return makeGroupComponent(group);
}
}
@ -1233,7 +1261,7 @@ public class StructureMapValidator extends BaseValidator {
grp.getInput().size() == 2 && grp.getInput().get(0).getMode() == StructureMapInputMode.SOURCE && grp.getInput().get(1).getMode() == StructureMapInputMode.TARGET) {
String srcT = resolveInputType(map, grp.getInput().get(0));
String tgtT = resolveInputType(map, grp.getInput().get(1));
if (sameTypes(srcT, srcType) && sameTypes(tgtT, tgtType)) {
if (sameTypes(srcT, srcType) && sameTypes(tgtT, tgtType) || sameTypes(srcT, srcUrl) && sameTypes(tgtT, tgtUrl)) {
return grp;
}
}