Skip to content

Commit

Permalink
Merge pull request #1140 from hapifhir/gg-202302-map-validation-3
Browse files Browse the repository at this point in the history
structuremap validation and invariant fixes for forthcoming R5 release
  • Loading branch information
grahamegrieve authored Feb 27, 2023
2 parents a62c868 + 4c30621 commit b0daf66
Show file tree
Hide file tree
Showing 9 changed files with 446 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.npm.NpmPackage;

Expand Down Expand Up @@ -115,6 +116,7 @@ protected void doPatchUrls(Resource resource) {
cr.setUrl(patchUrl(cr.getUrl(), cr.fhirType()));
if (cr instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) cr;
sd.setBaseDefinition(patchUrl(sd.getBaseDefinition(), sd.fhirType()));
new ProfileUtilities(null, null, null, null).setIds(sd, false);
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
Expand Down Expand Up @@ -152,6 +154,9 @@ private void patchUrls(OperationDefinitionParameterComponent param) {

private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
if (!Utilities.isAbsoluteUrl(tr.getCode())) {
tr.setCode(URL_BASE+versionString()+"/StructureDefinition/"+tr.getCode());
}
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(patchUrl(s.getValue(), "StructureDefinitino"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,21 @@ public String getChildValue(String name) {
if (name.equals(child.getName()))
return child.getValue();
}
for (Element child : children) {
if (name.equals(child.getNameBase()))
return child.getValue();
}
return null;
}

private String getNameBase() {
if (property.isChoice()) {
return property.getName().replace("[x]", "");
} else {
return getName();
}
}

public void setChildValue(String name, String value) {
if (children == null)
children = new ArrayList<Element>();
Expand Down Expand Up @@ -543,6 +555,16 @@ public Element makeElement(String name) throws FHIRException {
Element ne = new Element(name, p);
children.add(ne);
return ne;
} else if (p.getDefinition().isChoice() && name.startsWith(p.getName().replace("[x]", ""))) {
String type = name.substring(p.getName().length()-3);
if (new ContextUtilities(property.getContext()).isPrimitiveDatatype(Utilities.uncapitalize(type))) {
type = Utilities.uncapitalize(type);
}
Element ne = new Element(name, p);
ne.setType(type);
children.add(ne);
return ne;

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ private void parseRule(Element map, Element context, FHIRLexer lexer, boolean ne
rule.forceElement("source").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME);
rule.forceElement("target").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME);
rule.forceElement("target").makeElement("transform").setValue(StructureMapTransform.CREATE.toCode());
Element dep = rule.forceElement("dependent");
dep.makeElement("name").setValue(StructureMapUtilities.DEF_GROUP_NAME);
dep.makeElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME);
dep.makeElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME);
// no dependencies - imply what is to be done based on types
}
if (newFmt) {
Expand Down Expand Up @@ -480,7 +484,7 @@ private void parseTarget(Element rule, FHIRLexer lexer) throws FHIRException {
loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
target.setUserData(StructureMapUtilities.MAP_EXPRESSION, node);
target.addElement("parameter").markLocation(loc).setValue(node.toString());
target.addElement("parameter").markLocation(loc).makeElement("valueString").setValue(node.toString());
lexer.token(")");
} else if (lexer.hasToken("(")) {
target.makeElement("transform").markLocation(loc).setValue(name);
Expand Down Expand Up @@ -529,11 +533,11 @@ private void parseTarget(Element rule, FHIRLexer lexer) throws FHIRException {

private void parseParameter(Element ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError {
if (!lexer.isConstant()) {
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setType("string").setValue(lexer.take());
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueId").setValue(lexer.take());
} else if (lexer.isStringConstant())
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setType("string").setValue(lexer.readConstant("??"));
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueString").setValue(lexer.readConstant("??"));
else {
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setType("string").setValue(readConstant(lexer.take(), lexer));
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueString").setValue(readConstant(lexer.take(), lexer));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public class StructureMapUtilities {
public static final String MAP_EXPRESSION = "map.transform.expression";
private static final boolean RENDER_MULTIPLE_TARGETS_ONELINE = true;
public static final String AUTO_VAR_NAME = "vvv";
public static final String DEF_GROUP_NAME = "DefaultMappingGroupAnonymousAlias";

private final IWorkerContext worker;
private final FHIRPathEngine fpe;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,8 @@ public class I18nConstants {
public static final String SM_GROUP_INPUT_NO_TYPE = "SM_GROUP_INPUT_NO_TYPE";
public static final String SM_GROUP_INPUT_TYPE_NOT_DECLARED = "SM_GROUP_INPUT_TYPE_NOT_DECLARED";
public static final String SM_GROUP_INPUT_MODE_MISMATCH = "SM_GROUP_INPUT_MODE_MISMATCH";
public static final String SM_GROUP_INPUT_TYPE_UNKNOWN = "SM_GROUP_INPUT_TYPE_UNKNOWN";
public static final String SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE = "SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE";
public static final String SM_GROUP_INPUT_TYPE_UNKNOWN_TYPE = "SM_GROUP_INPUT_TYPE_UNKNOWN_TYPE";
public static final String SM_SOURCE_CONTEXT_UNKNOWN = "SM_SOURCE_CONTEXT_UNKNOWN";
public static final String SM_SOURCE_PATH_INVALID = "SM_SOURCE_PATH_INVALID";
public static final String SM_RULE_SOURCE_MIN_REDUNDANT = "SM_RULE_SOURCE_MIN_REDUNDANT";
Expand All @@ -804,6 +805,13 @@ public class I18nConstants {
public static final String SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = "SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE";
public static final String SM_TARGET_TRANSFORM_EXPRESSION_ERROR = "SM_TARGET_TRANSFORM_EXPRESSION_ERROR";
public static final String SM_IMPORT_NOT_FOUND = "SM_IMPORT_NOT_FOUND";
public static final String SM_TARGET_TYPE_MULTIPLE_POSSIBLE = "SM_TARGET_TYPE_MULTIPLE_POSSIBLE";
public static final String SM_DEPENDENT_PARAM_MODE_MISMATCH = "SM_DEPENDENT_PARAM_MODE_MISMATCH";
public static final String SM_DEPENDENT_PARAM_TYPE_MISMATCH = "SM_DEPENDENT_PARAM_TYPE_MISMATCH";
public static final String SM_ORPHAN_GROUP = "SM_ORPHAN_GROUP";
public static final String SM_SOURCE_TYPE_NOT_FOUND = "SM_SOURCE_TYPE_NOT_FOUND";
public static final String SM_TARGET_TYPE_NOT_FOUND = "SM_TARGET_TYPE_NOT_FOUND";
public static final String SM_MATCHING_RULEGROUP_NOT_FOUND = "SM_MATCHING_RULEGROUP_NOT_FOUND";
}


Expand Down
16 changes: 13 additions & 3 deletions org.hl7.fhir.utilities/src/main/resources/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,10 @@ SM_NAME_INVALID = The name {0} is not valid
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
SM_GROUP_INPUT_TYPE_NOT_DECLARED = The type {0} was not declared and is unknown
SM_GROUP_INPUT_TYPE_NOT_DECLARED = The type {0} is not declared and is unknown
SM_GROUP_INPUT_MODE_MISMATCH = The type {0} has mode {1} which doesn''t match the structure definition {2}
SM_GROUP_INPUT_TYPE_UNKNOWN = The type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
SM_GROUP_INPUT_TYPE_UNKNOWN_STRUCTURE = The type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
SM_GROUP_INPUT_TYPE_UNKNOWN_TYPE = The type {0} is not known, so the paths cannot be validated
SM_SOURCE_CONTEXT_UNKNOWN = The source context {0} is not known at this point
SM_SOURCE_PATH_INVALID = The source path {0}.{1} refers to the path {2} which is unknown
SM_RULE_SOURCE_MIN_REDUNDANT = The min value of {0} is redundant since the valid min is {0}
Expand All @@ -853,6 +854,15 @@ SM_TARGET_NO_TRANSFORM_NO_CHECKED = When there is no transform, parameters can''
SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = The value of the type parameter could not be processed
SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = The parameter at index {0} could not be processed (type = {1})
SM_TARGET_TRANSFORM_EXPRESSION_ERROR = The FHIRPath expression passed as the evaluate parameter is invalid: {0}
SM_IMPORT_NOT_FOUND = No maps were found to match {0} - validation may be wrong
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_ORPHAN_GROUP = This group 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
SM_MATCHING_RULEGROUP_NOT_FOUND = Unable to find a default rule for the type pair source={0} and target={1}




Original file line number Diff line number Diff line change
Expand Up @@ -3774,7 +3774,7 @@ private List<ElementDefinition> getCriteriaForDiscriminator(String path, Element
}

TypedElementDefinition ted = null;
String fp = FHIRPathExpressionFixer.fixExpr(discriminator, null);
String fp = FHIRPathExpressionFixer.fixExpr(discriminator, null, context.getVersion());
ExpressionNode expr = null;
try {
expr = fpe.parse(fp);
Expand Down Expand Up @@ -4392,7 +4392,7 @@ private boolean sliceMatches(ValidatorHostContext hostContext, Element element,
}

try {
n = fpe.parse(FHIRPathExpressionFixer.fixExpr(expression.toString(), null));
n = fpe.parse(FHIRPathExpressionFixer.fixExpr(expression.toString(), null, context.getVersion()));
} catch (FHIRLexerException e) {
if (STACK_TRACE) e.printStackTrace();
throw new FHIRException(context.formatMessage(I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, expression, profile.getVersionedUrl(), path, e.getMessage()));
Expand Down Expand Up @@ -6020,7 +6020,7 @@ private boolean checkInvariants(ValidatorHostContext hostContext, List<Validatio
}
List<ValidationMessage> invErrors = null;
// We key based on inv.expression rather than inv.key because expressions can change in derived profiles and aren't guaranteed to be consistent across profiles.
String key = FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey());
String key = FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey(), context.getVersion());
if (!invMap.keySet().contains(key)) {
invErrors = new ArrayList<ValidationMessage>();
invMap.put(key, invErrors);
Expand Down Expand Up @@ -6074,7 +6074,7 @@ public boolean checkInvariant(ValidatorHostContext hostContext, List<ValidationM
if (n == null) {
long t = System.nanoTime();
try {
String expr = FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey());
String expr = FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey(), context.getVersion());
n = fpe.parse(expr);
} catch (FHIRException e) {
rule(errors, NO_RULE_DATE, IssueType.INVARIANT, element.line(), element.col(), path, false, I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getVersionedUrl(), path, e.getMessage());
Expand Down Expand Up @@ -6285,7 +6285,7 @@ public void checkAllInvariants() {
try {
ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache");
if (n == null) {
n = fpe.parse(FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey()));
n = fpe.parse(FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey(), context.getVersion()));
inv.setUserData("validator.expression.cache", n);
}
fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n);
Expand Down
Loading

0 comments on commit b0daf66

Please sign in to comment.