Merge pull request #1160 from hapifhir/gg-202303-qa-sm-again
More fixes for structure map validation
This commit is contained in:
commit
f3572b9b8b
|
@ -2150,6 +2150,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
|
||||
List<StructureDefinition> res = new ArrayList<>();
|
||||
structures.listAll(res);
|
||||
res.removeIf(sd -> !sd.getType().equals(typeName));
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean isTlogging() {
|
||||
return tlogging;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.hl7.fhir.r5.context;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
|
@ -736,13 +737,21 @@ public interface IWorkerContext {
|
|||
/**
|
||||
* This is a short cut for fetchResource(StructureDefinition.class, ...)
|
||||
* but it accepts a typename - that is, it resolves based on StructureDefinition.type
|
||||
* or StructureDefinition.url
|
||||
* or StructureDefinition.url. This only resolves to http://hl7.org/fhir/StructureDefinition/{typename}
|
||||
*
|
||||
* @param typeName
|
||||
* @return
|
||||
*/
|
||||
public StructureDefinition fetchTypeDefinition(String typeName);
|
||||
|
||||
/**
|
||||
* This finds all the structure definitions that have the given typeName
|
||||
*
|
||||
* @param typeName
|
||||
* @return
|
||||
*/
|
||||
public List<StructureDefinition> fetchTypeDefinitions(String n);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a set of keys that can be used to get binaries from this context.
|
||||
|
|
|
@ -1340,6 +1340,29 @@ public String toString() {
|
|||
return getExpression()+" "+getDiagnostics()+" "+getSeverity().toCode()+"/"+getCode().toCode()+": "+getDetails().getText();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWarningOrMore() {
|
||||
switch (getSeverity()) {
|
||||
case FATAL: return true;
|
||||
case ERROR: return true;
|
||||
case WARNING: return true;
|
||||
case INFORMATION: return false;
|
||||
case SUCCESS: return false;
|
||||
case NULL: return false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
public boolean isInformationorLess() {
|
||||
switch (getSeverity()) {
|
||||
case FATAL: return false;
|
||||
case ERROR: return true;
|
||||
case WARNING: return false;
|
||||
case INFORMATION: return true;
|
||||
case SUCCESS: return true;
|
||||
case NULL: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
// end addition
|
||||
}
|
||||
|
||||
|
@ -1543,6 +1566,19 @@ public String toString() {
|
|||
return ResourceType.OperationOutcome;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSuccess() {
|
||||
for (OperationOutcomeIssueComponent iss : getIssue()) {
|
||||
if (iss.isWarningOrMore() || iss.getCode() != IssueType.INFORMATIONAL) {
|
||||
return false;
|
||||
}
|
||||
if (iss.isInformationorLess() || iss.getCode() != IssueType.INFORMATIONAL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -229,22 +229,34 @@ public class TypeDetails {
|
|||
tail = n.substring( n.indexOf("#")+1);
|
||||
tail = tail.substring(tail.indexOf("."));
|
||||
}
|
||||
String t = ProfiledType.ns(n);
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, t);
|
||||
while (sd != null) {
|
||||
if (tail == null && typesContains(sd.getUrl()))
|
||||
return true;
|
||||
if (tail == null && getSystemType(sd.getUrl()) != null && typesContains(getSystemType(sd.getUrl())))
|
||||
return true;
|
||||
if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
|
||||
return true;
|
||||
if (sd.hasBaseDefinition()) {
|
||||
if (sd.getType().equals("uri"))
|
||||
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");
|
||||
else
|
||||
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||
} else
|
||||
sd = null;
|
||||
List<StructureDefinition> list = new ArrayList<>();
|
||||
if (!Utilities.isAbsoluteUrl(n)) {
|
||||
list.addAll(context.fetchTypeDefinitions(n));
|
||||
} else {
|
||||
String t = ProfiledType.ns(n);
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, t);
|
||||
if (sd != null) {
|
||||
list.add(sd);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StructureDefinition sd = list.get(i);
|
||||
while (sd != null) {
|
||||
if (tail == null && typesContains(sd.getUrl()))
|
||||
return true;
|
||||
if (tail == null && getSystemType(sd.getUrl()) != null && typesContains(getSystemType(sd.getUrl())))
|
||||
return true;
|
||||
if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
|
||||
return true;
|
||||
if (sd.hasBaseDefinition()) {
|
||||
if (sd.getType().equals("uri"))
|
||||
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");
|
||||
else
|
||||
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||
} else {
|
||||
sd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -6045,7 +6045,12 @@ public class FHIRPathEngine {
|
|||
}
|
||||
|
||||
private boolean isAbstractType(List<TypeRefComponent> list) {
|
||||
return list.size() != 1 ? true : Utilities.existsInList(list.get(0).getCode(), "Element", "BackboneElement", "Resource", "DomainResource");
|
||||
if (list.size() != 1) {
|
||||
return false;
|
||||
} else {
|
||||
StructureDefinition sd = worker.fetchTypeDefinition(list.get(0).getCode());
|
||||
return sd != null && sd.getAbstract();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasType(ElementDefinition ed, String s) {
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
package org.hl7.fhir.r5.utils.structuremap;
|
||||
|
||||
import org.hl7.fhir.r5.model.StructureMap;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupComponent;
|
||||
|
||||
public class ResolvedGroup {
|
||||
public StructureMap.StructureMapGroupComponent target;
|
||||
public StructureMap targetMap;
|
||||
private StructureMap.StructureMapGroupComponent targetGroup;
|
||||
private StructureMap targetMap;
|
||||
|
||||
public ResolvedGroup(StructureMap targetMap, StructureMapGroupComponent targetGroup) {
|
||||
super();
|
||||
this.targetMap = targetMap;
|
||||
this.targetGroup = targetGroup;
|
||||
}
|
||||
|
||||
public StructureMap.StructureMapGroupComponent getTargetGroup() {
|
||||
return targetGroup;
|
||||
}
|
||||
public StructureMap getTargetMap() {
|
||||
return targetMap;
|
||||
}
|
||||
|
||||
public void setTargetGroup(StructureMap.StructureMapGroupComponent targetGroup) {
|
||||
this.targetGroup = targetGroup;
|
||||
}
|
||||
|
||||
public void setTargetMap(StructureMap targetMap) {
|
||||
this.targetMap = targetMap;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1243,7 +1243,7 @@ public class StructureMapUtilities {
|
|||
// todo: check inputs
|
||||
if (group.hasExtends()) {
|
||||
ResolvedGroup rg = resolveGroupReference(map, group, group.getExtends());
|
||||
executeGroup(indent + " ", context, rg.targetMap, vars, rg.target, false);
|
||||
executeGroup(indent + " ", context, rg.getTargetMap(), vars, rg.getTargetGroup(), false);
|
||||
}
|
||||
|
||||
for (StructureMapGroupRuleComponent r : group.getRule()) {
|
||||
|
@ -1279,9 +1279,9 @@ public class StructureMapUtilities {
|
|||
String tgtType = tgt.fhirType();
|
||||
ResolvedGroup defGroup = resolveGroupByTypes(map, rule.getName(), group, srcType, tgtType);
|
||||
Variables vdef = new Variables();
|
||||
vdef.add(VariableMode.INPUT, defGroup.target.getInput().get(0).getName(), src);
|
||||
vdef.add(VariableMode.OUTPUT, defGroup.target.getInput().get(1).getName(), tgt);
|
||||
executeGroup(indent + " ", context, defGroup.targetMap, vdef, defGroup.target, false);
|
||||
vdef.add(VariableMode.INPUT, defGroup.getTargetGroup().getInput().get(0).getName(), src);
|
||||
vdef.add(VariableMode.OUTPUT, defGroup.getTargetGroup().getInput().get(1).getName(), tgt);
|
||||
executeGroup(indent + " ", context, defGroup.getTargetMap(), vdef, defGroup.getTargetGroup(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1290,12 +1290,12 @@ public class StructureMapUtilities {
|
|||
private void executeDependency(String indent, TransformContext context, StructureMap map, Variables vin, StructureMapGroupComponent group, StructureMapGroupRuleDependentComponent dependent) throws FHIRException {
|
||||
ResolvedGroup rg = resolveGroupReference(map, group, dependent.getName());
|
||||
|
||||
if (rg.target.getInput().size() != dependent.getParameter().size()) {
|
||||
throw new FHIRException("Rule '" + dependent.getName() + "' has " + rg.target.getInput().size() + " but the invocation has " + dependent.getParameter().size() + " variables");
|
||||
if (rg.getTargetGroup().getInput().size() != dependent.getParameter().size()) {
|
||||
throw new FHIRException("Rule '" + dependent.getName() + "' has " + rg.getTargetGroup().getInput().size() + " but the invocation has " + dependent.getParameter().size() + " variables");
|
||||
}
|
||||
Variables v = new Variables();
|
||||
for (int i = 0; i < rg.target.getInput().size(); i++) {
|
||||
StructureMapGroupInputComponent input = rg.target.getInput().get(i);
|
||||
for (int i = 0; i < rg.getTargetGroup().getInput().size(); i++) {
|
||||
StructureMapGroupInputComponent input = rg.getTargetGroup().getInput().get(i);
|
||||
StructureMapGroupRuleTargetParameterComponent rdp = dependent.getParameter().get(i);
|
||||
String var = rdp.getValue().primitiveValue();
|
||||
VariableMode mode = input.getMode() == StructureMapInputMode.SOURCE ? VariableMode.INPUT : VariableMode.OUTPUT;
|
||||
|
@ -1306,7 +1306,7 @@ public class StructureMapUtilities {
|
|||
throw new FHIRException("Rule '" + dependent.getName() + "' " + mode.toString() + " variable '" + input.getName() + "' named as '" + var + "' has no value (vars = " + vin.summary() + ")");
|
||||
v.add(mode, input.getName(), vv);
|
||||
}
|
||||
executeGroup(indent + " ", context, rg.targetMap, v, rg.target, false);
|
||||
executeGroup(indent + " ", context, rg.getTargetMap(), v, rg.getTargetGroup(), false);
|
||||
}
|
||||
|
||||
private String determineTypeFromSourceType(StructureMap map, StructureMapGroupComponent source, Base base, String[] types) throws FHIRException {
|
||||
|
@ -1315,20 +1315,18 @@ public class StructureMapUtilities {
|
|||
if (source.hasUserData(kn))
|
||||
return source.getUserString(kn);
|
||||
|
||||
ResolvedGroup res = new ResolvedGroup();
|
||||
res.targetMap = null;
|
||||
res.target = null;
|
||||
ResolvedGroup res = new ResolvedGroup(null, null);
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if (matchesByType(map, grp, type)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = map;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(map);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches looking for default rule for '" + type + "'");
|
||||
}
|
||||
}
|
||||
if (res.targetMap != null) {
|
||||
String result = getActualType(res.targetMap, res.target.getInput().get(1).getType());
|
||||
if (res.getTargetMap() != null) {
|
||||
String result = getActualType(res.getTargetMap(), res.getTargetGroup().getInput().get(1).getType());
|
||||
source.setUserData(kn, result);
|
||||
return result;
|
||||
}
|
||||
|
@ -1341,19 +1339,19 @@ public class StructureMapUtilities {
|
|||
if (!impMap.getUrl().equals(map.getUrl())) {
|
||||
for (StructureMapGroupComponent grp : impMap.getGroup()) {
|
||||
if (matchesByType(impMap, grp, type)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = impMap;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(impMap);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches for default rule for '" + type + "' in " + res.targetMap.getUrl() + " (" + res.target.getName() + ") and " + impMap.getUrl() + " (" + grp.getName() + ")");
|
||||
throw new FHIRException("Multiple possible matches for default rule for '" + type + "' in " + res.getTargetMap().getUrl() + " (" + res.getTargetGroup().getName() + ") and " + impMap.getUrl() + " (" + grp.getName() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res.target == null)
|
||||
if (res.getTargetGroup() == null)
|
||||
throw new FHIRException("No matches found for default rule for '" + type + "' from " + map.getUrl());
|
||||
String result = getActualType(res.targetMap, res.target.getInput().get(1).getType()); // should be .getType, but R2...
|
||||
String result = getActualType(res.getTargetMap(), res.getTargetGroup().getInput().get(1).getType()); // should be .getType, but R2...
|
||||
source.setUserData(kn, result);
|
||||
return result;
|
||||
}
|
||||
|
@ -1390,19 +1388,17 @@ public class StructureMapUtilities {
|
|||
if (source.hasUserData(kn))
|
||||
return (ResolvedGroup) source.getUserData(kn);
|
||||
|
||||
ResolvedGroup res = new ResolvedGroup();
|
||||
res.targetMap = null;
|
||||
res.target = null;
|
||||
ResolvedGroup res = new ResolvedGroup(null, null);
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if (matchesByType(map, grp, srcType, tgtType)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = map;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(map);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches looking for rule for '" + srcType + "/" + tgtType + "', from rule '" + ruleid + "'");
|
||||
}
|
||||
}
|
||||
if (res.targetMap != null) {
|
||||
if (res.getTargetMap() != null) {
|
||||
source.setUserData(kn, res);
|
||||
return res;
|
||||
}
|
||||
|
@ -1415,17 +1411,17 @@ public class StructureMapUtilities {
|
|||
if (!impMap.getUrl().equals(map.getUrl())) {
|
||||
for (StructureMapGroupComponent grp : impMap.getGroup()) {
|
||||
if (matchesByType(impMap, grp, srcType, tgtType)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = impMap;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(impMap);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches for rule for '" + srcType + "/" + tgtType + "' in " + res.targetMap.getUrl() + " and " + impMap.getUrl() + ", from rule '" + ruleid + "'");
|
||||
throw new FHIRException("Multiple possible matches for rule for '" + srcType + "/" + tgtType + "' in " + res.getTargetMap().getUrl() + " and " + impMap.getUrl() + ", from rule '" + ruleid + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res.target == null)
|
||||
if (res.getTargetGroup() == null)
|
||||
throw new FHIRException("No matches found for rule for '" + srcType + " to " + tgtType + "' from " + map.getUrl() + ", from rule '" + ruleid + "'");
|
||||
source.setUserData(kn, res);
|
||||
return res;
|
||||
|
@ -1493,19 +1489,17 @@ public class StructureMapUtilities {
|
|||
if (source.hasUserData(kn))
|
||||
return (ResolvedGroup) source.getUserData(kn);
|
||||
|
||||
ResolvedGroup res = new ResolvedGroup();
|
||||
res.targetMap = null;
|
||||
res.target = null;
|
||||
ResolvedGroup res = new ResolvedGroup(null, null);
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if (grp.getName().equals(name)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = map;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(map);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches for rule '" + name + "'");
|
||||
}
|
||||
}
|
||||
if (res.targetMap != null) {
|
||||
if (res.getTargetMap() != null) {
|
||||
source.setUserData(kn, res);
|
||||
return res;
|
||||
}
|
||||
|
@ -1518,19 +1512,19 @@ public class StructureMapUtilities {
|
|||
if (!impMap.getUrl().equals(map.getUrl())) {
|
||||
for (StructureMapGroupComponent grp : impMap.getGroup()) {
|
||||
if (grp.getName().equals(name)) {
|
||||
if (res.targetMap == null) {
|
||||
res.targetMap = impMap;
|
||||
res.target = grp;
|
||||
if (res.getTargetMap() == null) {
|
||||
res.setTargetMap(impMap);
|
||||
res.setTargetGroup(grp);
|
||||
} else
|
||||
throw new FHIRException("Multiple possible matches for rule group '" + name + "' in " +
|
||||
res.targetMap.getUrl() + "#" + res.target.getName() + " and " +
|
||||
res.getTargetMap().getUrl() + "#" + res.getTargetGroup().getName() + " and " +
|
||||
impMap.getUrl() + "#" + grp.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res.target == null)
|
||||
if (res.getTargetGroup() == null)
|
||||
throw new FHIRException("No matches found for rule '" + name + "'. Reference found in " + map.getUrl());
|
||||
source.setUserData(kn, res);
|
||||
return res;
|
||||
|
|
|
@ -846,6 +846,7 @@ public class I18nConstants {
|
|||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM = "CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID = "CONCEPTMAP_GROUP_TARGET_PROPERTY_CODE_INVALID";
|
||||
public static final String CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM = "CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_UNKNOWN_SYSTEM";
|
||||
public static final String SM_GROUP_NAME_DUPLICATE = "SM_GROUP_NAME_DUPLICATE";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -577,7 +577,7 @@ FHIRPATH_LOCATION = (at {0})
|
|||
FHIRPATH_UNKNOWN_CONTEXT = Unknown context evaluating FHIRPath expression: {0}
|
||||
FHIRPATH_UNKNOWN_CONTEXT_ELEMENT = Unknown context element evaluating FHIRPath expression: {0}
|
||||
FHIRPATH_ALIAS_COLLECTION = Attempt to alias a collection, not a singleton evaluating FHIRPath expression
|
||||
FHIRPATH_UNKNOWN_NAME = Error evaluating FHIRPath expression: The name {0} is not valid for any of the possible types: {1}
|
||||
FHIRPATH_UNKNOWN_NAME = Error evaluating FHIRPath expression: The name ''{0}'' is not valid for any of the possible types: {1}
|
||||
FHIRPATH_UNKNOWN_CONSTANT = Error evaluating FHIRPath expression: Invalid FHIR Constant {0}
|
||||
FHIRPATH_CANNOT_USE = Error evaluating FHIRPath expression: Cannot use {0} in this context because {1}
|
||||
FHIRPATH_CANT_COMPARE = Error evaluating FHIRPath expression: Unable to compare values of type {0} and {1}
|
||||
|
@ -834,6 +834,7 @@ SD_NO_SLICING_ON_ROOT = Slicing is not allowed at the root of a profile
|
|||
REFERENCE_REF_QUERY_INVALID = The query part of the conditional reference is not a valid query string ({0})
|
||||
SM_RULEGROUP_NOT_FOUND = The group {0} could not be resolved
|
||||
SM_NAME_INVALID = The name {0} is not valid
|
||||
SM_GROUP_NAME_DUPLICATE = The Group name ''{0}'' is already used
|
||||
SM_GROUP_INPUT_DUPLICATE = The name {0} is already used
|
||||
SM_GROUP_INPUT_MODE_INVALID = The group parameter {0} mode {1} isn''t valid
|
||||
SM_GROUP_INPUT_NO_TYPE = The group parameter {0} has no type, so the paths cannot be validated
|
||||
|
@ -846,7 +847,7 @@ SM_SOURCE_PATH_INVALID = The source path {0}.{1} refers to the path {2} which is
|
|||
SM_RULE_SOURCE_MIN_REDUNDANT = The min value of {0} is redundant since the valid min is {0}
|
||||
SM_RULE_SOURCE_MAX_REDUNDANT = The max value of {0} is redundant since the valid max is {0}
|
||||
SM_RULE_SOURCE_LISTMODE_REDUNDANT = The listMode value of {0} is redundant since the valid max is {0}
|
||||
SM_TARGET_CONTEXT_UNKNOWN = The target context {0} is not known at this point
|
||||
SM_TARGET_CONTEXT_UNKNOWN = The target context ''{0}'' is not known at this point
|
||||
SM_TARGET_PATH_INVALID = The target path {0}.{1} refers to the path {2} which is unknown
|
||||
SM_NO_LIST_MODE_NEEDED = A list mode should not be provided since this is a rule that can only be executed once
|
||||
SM_NO_LIST_RULE_ID_NEEDED = A list ruleId should not be provided since this is a rule that can only be executed once
|
||||
|
@ -864,7 +865,7 @@ SM_TARGET_TRANSFORM_EXPRESSION_ERROR = The FHIRPath expression passed as the eva
|
|||
SM_IMPORT_NOT_FOUND = No maps were found to match {0} - validation may be wrong
|
||||
SM_TARGET_TYPE_MULTIPLE_POSSIBLE = Multiple types are possible here ({0}) so further type checking is not possible
|
||||
SM_DEPENDENT_PARAM_MODE_MISMATCH = The parameter {0} refers to the variable {1} but it''s mode is {2} which is not the same as the mode required for the group {3}
|
||||
SM_DEPENDENT_PARAM_TYPE_MISMATCH = The parameter {0} refers to the variable {1} but it''s type is {2} which is not compatible with the type required for the group {3}
|
||||
SM_DEPENDENT_PARAM_TYPE_MISMATCH = The parameter ''{0}'' refers to the variable ''{1}'' but it''s type is ''{2}'' which is not compatible with the type required for the group ''{3}'', which is ''{4}'' (from map ''{5}'')
|
||||
SM_ORPHAN_GROUP = The group {0} is not called from within this mapping script, and does not have types on it's inputs, so type verification is not possible
|
||||
SM_SOURCE_TYPE_NOT_FOUND = No source type was found, so the default group for this implied dependent rule could not be determined
|
||||
SM_TARGET_TYPE_NOT_FOUND = No target type was found, so the default group for this implied dependent rule could not be determined
|
||||
|
|
|
@ -26,17 +26,29 @@ public class CompactRenderer extends ValidationOutputRenderer {
|
|||
@Override
|
||||
public void render(OperationOutcome op) throws IOException {
|
||||
if (split) {
|
||||
String file = Utilities.changeFileExt(tail(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)), ".txt");
|
||||
PrintStream dstF = new PrintStream(new FileOutputStream(Utilities.path(dir.getAbsolutePath(), file)));
|
||||
render(dstF, op);
|
||||
dstF.close();
|
||||
File file = new File(Utilities.path(dir.getAbsolutePath(), Utilities.changeFileExt(tail(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)), ".txt")));
|
||||
if (op.isSuccess()) {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
} else {
|
||||
PrintStream dstF = new PrintStream(new FileOutputStream(file));
|
||||
render(dstF, op);
|
||||
dstF.close();
|
||||
}
|
||||
} else {
|
||||
render(dst, op);
|
||||
}
|
||||
}
|
||||
|
||||
private void render(PrintStream d, OperationOutcome op) {
|
||||
d.println(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)+" "+getRunDate());
|
||||
if (split) {
|
||||
d.println(new File(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)).getName()+" "+getRunDate()+":");
|
||||
} else {
|
||||
d.println();
|
||||
d.println("----------------------------------------------------------------------------------");
|
||||
d.println(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)+" "+getRunDate());
|
||||
}
|
||||
List<String> lines = new ArrayList<>();
|
||||
for (OperationOutcome.OperationOutcomeIssueComponent issue : op.getIssue()) {
|
||||
String path = issue.hasExpression() ? issue.getExpression().get(0).asStringValue() : "n/a";
|
||||
|
@ -48,6 +60,10 @@ public class CompactRenderer extends ValidationOutputRenderer {
|
|||
for (String s : lines) {
|
||||
d.println(s.substring(s.indexOf("|")+1));
|
||||
}
|
||||
if (split) {
|
||||
} else {
|
||||
d.println();
|
||||
}
|
||||
}
|
||||
|
||||
private String tail(String n) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
|||
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.r5.utils.structuremap.ResolvedGroup;
|
||||
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
@ -173,6 +174,8 @@ public class StructureMapValidator extends BaseValidator {
|
|||
if (ed != null) {
|
||||
if (!ed.getPath().contains(".")) {
|
||||
return ed.getPath();
|
||||
} else if (isAbstractType(ed.getType())) {
|
||||
return sd.getUrl()+"#"+ed.getPath();
|
||||
} else if (ed.getType().size() == 1) {
|
||||
return ed.getType().get(0).getWorkingCode();
|
||||
}
|
||||
|
@ -305,6 +308,16 @@ public class StructureMapValidator extends BaseValidator {
|
|||
|
||||
}
|
||||
|
||||
public boolean isAbstractType(List<TypeRefComponent> list) {
|
||||
if (list.size() != 1) {
|
||||
return false;
|
||||
} else {
|
||||
StructureDefinition sd = context.fetchTypeDefinition(list.get(0).getCode());
|
||||
return sd != null && sd.getAbstract();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean validateStructureMap(List<ValidationMessage> errors, Element src, NodeStack stack) {
|
||||
boolean ok = true;
|
||||
List<Element> imports = src.getChildrenByName("import");
|
||||
|
@ -314,6 +327,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
cc++;
|
||||
}
|
||||
|
||||
List<String> grpNames = new ArrayList<>();
|
||||
List<Element> groups = src.getChildrenByName("group");
|
||||
// we iterate the groups repeatedly, validating them if they have stated types or found types, until nothing happens
|
||||
boolean fired = false;
|
||||
|
@ -325,7 +339,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)) && ok;
|
||||
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
|
||||
}
|
||||
}
|
||||
cc++;
|
||||
|
@ -336,7 +350,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)) && ok;
|
||||
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null), grpNames) && ok;
|
||||
}
|
||||
cc++;
|
||||
}
|
||||
|
@ -369,13 +383,16 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean validateGroup(List<ValidationMessage> errors, Element src, Element group, NodeStack stack) {
|
||||
private boolean validateGroup(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)) {
|
||||
grpNames.add(name);
|
||||
}
|
||||
|
||||
Element extend = group.getNamedChild("extends");
|
||||
if (extend != null) {
|
||||
StructureMapGroupComponent grp = resolveGroup(extend.primitiveValue(), src);
|
||||
ResolvedGroup grp = resolveGroup(extend.primitiveValue(), src);
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, extend.line(), extend.col(), stack.push(extend, -1, null, null).getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, extend.primitiveValue())) {
|
||||
// check inputs
|
||||
} else {
|
||||
|
@ -406,7 +423,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private StructureMapGroupComponent resolveGroup(String grpName, Element src) {
|
||||
private ResolvedGroup resolveGroup(String grpName, Element src) {
|
||||
if (grpName == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -414,13 +431,13 @@ public class StructureMapValidator extends BaseValidator {
|
|||
for (Element group : groups) {
|
||||
String name = group.getChildValue("name");
|
||||
if (grpName.equals(name)) {
|
||||
return makeGroupComponent(group);
|
||||
}
|
||||
return new ResolvedGroup(null, makeGroupComponent(group));
|
||||
}
|
||||
}
|
||||
for (StructureMap map : imports) {
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if (grpName.equals(grp.getName())) {
|
||||
return grp;
|
||||
return new ResolvedGroup(map, grp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -724,7 +741,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
String exp = params.get(0).getChildValue("value");
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), exp != null, I18nConstants.SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE, "0", params.size())) {
|
||||
try {
|
||||
TypeDetails td = fpe.check(variables, v.getSd().getType(), v.getEd().getPath(), fpe.parse(exp));
|
||||
TypeDetails td = fpe.check(variables, v.getSd().getUrl(), v.getEd().getPath(), fpe.parse(exp));
|
||||
if (td.getTypes().size() == 1) {
|
||||
type = td.getType();
|
||||
}
|
||||
|
@ -976,7 +993,9 @@ public class StructureMapValidator extends BaseValidator {
|
|||
|
||||
private void getElementDefinitionChildrenFromTypes(List<ElementDefinitionSource> result, StructureDefinition sd, ElementDefinition ed, String type, String element) {
|
||||
for (TypeRefComponent td : ed.getType()) {
|
||||
if (type == null | td.getWorkingCode().equals(type)) {
|
||||
String tn = td.getWorkingCode();
|
||||
StructureDefinition sdt = context.fetchTypeDefinition(tn);
|
||||
if (type == null || tn.equals(type) || (sdt != null && sdt.getType().equals(type))) {
|
||||
StructureDefinition tsd = context.fetchTypeDefinition(td.getWorkingCode());
|
||||
if (tsd != null) {
|
||||
for (ElementDefinition t : tsd.getSnapshot().getElement()) {
|
||||
|
@ -1027,20 +1046,22 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
StructureMapGroupComponent grp = resolveGroup(name, src);
|
||||
ResolvedGroup grp = resolveGroup(name, src);
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, name)) {
|
||||
List<Element> params = dependent.getChildren("parameter");
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), params.size() == grp.getInput().size(), I18nConstants.SM_RULEGROUP_NOT_FOUND, params.size(), grp.getInput().size())) {
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), params.size() == grp.getTargetGroup().getInput().size(), I18nConstants.SM_RULEGROUP_NOT_FOUND, params.size(), grp.getTargetGroup().getInput().size())) {
|
||||
VariableSet lvars = new VariableSet();
|
||||
int cc = 0;
|
||||
for (Element param : params) {
|
||||
NodeStack pstack = stack.push(param, cc, null, null);
|
||||
StructureMapGroupInputComponent input = grp.getInput().get(cc);
|
||||
StructureMapGroupInputComponent input = grp.getTargetGroup().getInput().get(cc);
|
||||
String iType = resolveType(grp, input, src);
|
||||
String pname = input.getName();
|
||||
VariableDefn v = getParameter(errors, param, pstack, variables, input.getMode());
|
||||
if (v != null) {
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), v.mode.equals(input.getMode().toCode()), I18nConstants.SM_DEPENDENT_PARAM_MODE_MISMATCH, param.getChildValue("name"), v.mode, input.getMode().toCode()) &&
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), typesMatch(v, input.getType()), I18nConstants.SM_DEPENDENT_PARAM_TYPE_MISMATCH, param.getChildValue("name"), v, input.getType())) {
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), v.mode.equals(input.getMode().toCode()), I18nConstants.SM_DEPENDENT_PARAM_MODE_MISMATCH, param.getChildValue("name"), v.mode, input.getMode().toCode(), grp.getTargetGroup().getName()) &&
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, param.line(), param.col(), pstack.getLiteralPath(), typesMatch(v, iType), I18nConstants.SM_DEPENDENT_PARAM_TYPE_MISMATCH,
|
||||
pname, v.summary(), input.getType(), grp.getTargetGroup().getName(), input.getType(), grp.getTargetMap() == null ? "$this" : grp.getTargetMap().getVersionedUrl())) {
|
||||
lvars.add(pname, v);
|
||||
} else {
|
||||
ok = false;
|
||||
|
@ -1050,11 +1071,11 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
cc++;
|
||||
}
|
||||
if (ok && grp.hasUserData("element.source")) {
|
||||
Element g = (Element) grp.getUserData("element.source");
|
||||
if (ok && grp.getTargetGroup().hasUserData("element.source")) {
|
||||
Element g = (Element) grp.getTargetGroup().getUserData("element.source");
|
||||
if (g.hasUserData("structuremap.parameters")) {
|
||||
VariableSet pvars = (VariableSet) g.getUserData("structuremap.parameters");
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), pvars.matches(lvars), I18nConstants.SM_DEPENDENT_PARAM_TYPE_MISMATCH_DUPLICATE, grp.getName(), pvars.summary(), lvars.summary());
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), pvars.matches(lvars), I18nConstants.SM_DEPENDENT_PARAM_TYPE_MISMATCH_DUPLICATE, grp.getTargetGroup().getName(), pvars.summary(), lvars.summary());
|
||||
} else {
|
||||
g.setUserData("structuremap.parameters", lvars);
|
||||
}
|
||||
|
@ -1067,6 +1088,25 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String resolveType(ResolvedGroup grp, StructureMapGroupInputComponent input, Element map) {
|
||||
if (grp.getTargetMap() == null) {
|
||||
List<Element> structures = map.getChildrenByName("structure");
|
||||
for (Element structure : structures) {
|
||||
String alias = structure.getChildValue("alias");
|
||||
if (alias != null && alias.equals(input.getType())) {
|
||||
return structure.getChildValue("url");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (StructureMapStructureComponent struc : grp.getTargetMap().getStructure()) {
|
||||
if (struc.hasAlias() && struc.getAlias().equals(input.getType())) {
|
||||
return struc.getUrl();
|
||||
}
|
||||
}
|
||||
}
|
||||
return input.getType();
|
||||
}
|
||||
|
||||
private StructureMapGroupComponent findDefaultGroup(Element src, String srcType, String tgtType) {
|
||||
List<Element> groups = src.getChildrenByName("group");
|
||||
for (Element group : groups) {
|
||||
|
@ -1126,9 +1166,16 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
private boolean typesMatch(VariableDefn v, String type) {
|
||||
if (type == null) {
|
||||
if (type == null || !v.hasTypeInfo()) {
|
||||
return true;
|
||||
} else if (v.getSd().getUrl().equals(type) || v.getSd().getType().equals(type)) {
|
||||
return true;
|
||||
} else {
|
||||
for (TypeRefComponent tr : v.getEd().getType()) {
|
||||
if (type.equals(tr.getWorkingCode()) || type.equals("http://hl7.org/fhir/StructureDefinition/"+tr.getWorkingCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -19,7 +19,7 @@
|
|||
|
||||
<properties>
|
||||
<hapi_fhir_version>6.2.1</hapi_fhir_version>
|
||||
<validator_test_case_version>1.2.16</validator_test_case_version>
|
||||
<validator_test_case_version>1.2.17-SNAPSHOT</validator_test_case_version>
|
||||
<junit_jupiter_version>5.7.1</junit_jupiter_version>
|
||||
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
|
||||
<maven_surefire_version>3.0.0-M5</maven_surefire_version>
|
||||
|
|
Loading…
Reference in New Issue