structuremap validation and invariant fixes for forthcoming R5 release
This commit is contained in:
parent
2bd73c650e
commit
4c30621491
|
@ -18,6 +18,7 @@ import org.hl7.fhir.r5.model.UriType;
|
|||
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;
|
||||
|
||||
|
@ -115,6 +116,7 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
|
|||
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())
|
||||
|
@ -152,6 +154,9 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
|
|||
|
||||
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"));
|
||||
}
|
||||
|
|
|
@ -292,9 +292,21 @@ public class Element extends Base {
|
|||
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>();
|
||||
|
@ -543,6 +555,16 @@ public class Element extends Base {
|
|||
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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -354,6 +354,10 @@ public class FmlParser extends ParserBase {
|
|||
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) {
|
||||
|
@ -480,7 +484,7 @@ public class FmlParser extends ParserBase {
|
|||
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);
|
||||
|
@ -529,11 +533,11 @@ public class FmlParser extends ParserBase {
|
|||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
@ -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}
|
||||
|
||||
|
||||
|
||||
|
|
@ -3774,7 +3774,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
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);
|
||||
|
@ -4392,7 +4392,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
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()));
|
||||
|
@ -6020,7 +6020,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
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);
|
||||
|
@ -6074,7 +6074,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
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());
|
||||
|
@ -6285,7 +6285,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
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);
|
||||
|
|
|
@ -1,66 +1,37 @@
|
|||
package org.hl7.fhir.validation.instance.type;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode;
|
||||
import org.hl7.fhir.r5.model.Extension;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
|
||||
import org.hl7.fhir.r5.model.StructureMap;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupComponent;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupInputComponent;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupTypeMode;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapInputMode;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
|
||||
import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
|
||||
import org.hl7.fhir.r5.model.TypeDetails;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.ElementDefinitionMatch;
|
||||
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.TimeTracker;
|
||||
import org.hl7.fhir.validation.instance.type.StructureMapValidator.ElementDefinitionSource;
|
||||
import org.hl7.fhir.validation.instance.type.StructureMapValidator.RuleInformation;
|
||||
import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableDefn;
|
||||
import org.hl7.fhir.validation.instance.type.StructureMapValidator.VariableSet;
|
||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
import org.jvnet.jaxb2_commons.xml.bind.annotation.adapters.CommaDelimitedStringAdapter;
|
||||
|
||||
public class StructureMapValidator extends BaseValidator {
|
||||
|
||||
|
@ -84,6 +55,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
public class RuleInformation {
|
||||
|
||||
int maxCount = 1;
|
||||
String defVariable;
|
||||
|
||||
public void seeCardinality(int max) {
|
||||
if (max == Integer.MAX_VALUE || maxCount == Integer.MAX_VALUE) {
|
||||
|
@ -100,8 +72,14 @@ public class StructureMapValidator extends BaseValidator {
|
|||
public int getMaxCount() {
|
||||
return maxCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getDefVariable() {
|
||||
return defVariable;
|
||||
}
|
||||
|
||||
public void setDefVariable(String defVariable) {
|
||||
this.defVariable = defVariable;
|
||||
}
|
||||
}
|
||||
|
||||
public class VariableDefn {
|
||||
|
@ -169,6 +147,16 @@ public class StructureMapValidator extends BaseValidator {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
public String getWorkingType() {
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
if (ed != null && ed.getType().size() == 1) {
|
||||
return ed.getType().get(0).getWorkingCode();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class VariableSet {
|
||||
|
@ -222,7 +210,11 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void add(String pname, VariableDefn v) {
|
||||
VariableDefn vn = v.copy();
|
||||
vn.name = pname;
|
||||
list.add(vn);
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean SOURCE = true;
|
||||
|
@ -254,14 +246,44 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
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;
|
||||
do {
|
||||
fired = false;
|
||||
cc = 0;
|
||||
for (Element group : groups) {
|
||||
if (!group.hasUserData("structuremap.validated")) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
cc++;
|
||||
}
|
||||
} while (fired);
|
||||
|
||||
cc = 0;
|
||||
for (Element group : groups) {
|
||||
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null)) && ok;
|
||||
if (!group.hasUserData("structuremap.validated")) {
|
||||
hint(errors, "2023-03-01", IssueType.INFORMATIONAL, group.line(), group.col(), stack.getLiteralPath(), ok, I18nConstants.SM_ORPHAN_GROUP);
|
||||
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null)) && ok;
|
||||
}
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean hasInputTypes(Element group) {
|
||||
List<Element> inputs = group.getChildrenByName("input");
|
||||
for (Element input : inputs) {
|
||||
if (!input.hasChild("type")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateImport(List<ValidationMessage> errors, Element src, Element import_, NodeStack stack) {
|
||||
String url = import_.primitiveValue();
|
||||
boolean ok = false;
|
||||
|
@ -293,13 +315,14 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
VariableSet variables = new VariableSet();
|
||||
VariableSet pvars = (VariableSet) group.getUserData("structuremap.parameters");
|
||||
|
||||
// first, load all the inputs
|
||||
List<Element> inputs = group.getChildrenByName("input");
|
||||
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) && ok;
|
||||
ok = validateInput(errors, src, group, input, stack.push(input, cc, null, null), structures, variables, pvars) && ok;
|
||||
cc++;
|
||||
}
|
||||
|
||||
|
@ -337,6 +360,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
|
||||
private StructureMapGroupComponent makeGroupComponent(Element group) {
|
||||
StructureMapGroupComponent grp = new StructureMapGroupComponent();
|
||||
grp.setUserData("element.source", group);
|
||||
grp.setName(group.getChildValue("name"));
|
||||
List<Element> inputs = group.getChildrenByName("input");
|
||||
for (Element input : inputs) {
|
||||
|
@ -352,27 +376,55 @@ 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) {
|
||||
private boolean validateInput(List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables, VariableSet pvars) {
|
||||
boolean ok = false;
|
||||
String name = input.getChildValue("name");
|
||||
String type = input.getChildValue("type");
|
||||
String mode = input.getChildValue("mode");
|
||||
String type = input.getChildValue("type");
|
||||
VariableDefn pv = null;
|
||||
if (type == null && pvars != null) {
|
||||
pv = pvars.getVariable(name, mode.equals("source"));
|
||||
if (pv != null) {
|
||||
type = pv.getWorkingType();
|
||||
}
|
||||
}
|
||||
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name) && // the name {0} is not valid)
|
||||
rule(errors, "2023-03-01", IssueType.DUPLICATE, input.line(), input.col(), stack.getLiteralPath(), !variables.hasVariable(name), I18nConstants.SM_GROUP_INPUT_DUPLICATE, name)) { // the name {0} is not valid)
|
||||
VariableDefn v = variables.add(name, mode);
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), Utilities.existsInList(mode, "source", "target"), I18nConstants.SM_GROUP_INPUT_MODE_INVALID, name, mode) && // the group parameter {0} mode {1} isn't valid
|
||||
warning(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_GROUP_INPUT_NO_TYPE, name)) { // the group parameter {0} has no type, so the paths cannot be validated
|
||||
Element structure = findStructure(structures, type);
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), structure != null, I18nConstants.SM_GROUP_INPUT_TYPE_NOT_DECLARED, type)) { // the type {0} was not declared and is unknown
|
||||
String url = structure.getChildValue("url");
|
||||
String smode = structure.getChildValue("mode");
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), mode.equals(smode), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode) && // the type {0} has mode {1} which doesn't match the structure definition {2}
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN, type, url)) { // the type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
|
||||
v.setType(1, sd, sd.getSnapshot().getElementFirstRep(), null);
|
||||
ok = true;
|
||||
String smode = null;
|
||||
StructureDefinition sd = null;
|
||||
ElementDefinition ed = null;
|
||||
if (pv != null) {
|
||||
sd = pv.getSd();
|
||||
ed = pv.getEd();
|
||||
} else {
|
||||
Element structure = findStructure(structures, type);
|
||||
if (structure != null) {
|
||||
smode = structure.getChildValue("mode");
|
||||
String url = structure.getChildValue("url");
|
||||
sd = context.fetchResource(StructureDefinition.class, url);
|
||||
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);
|
||||
}
|
||||
} else if (type != null) {
|
||||
sd = context.fetchTypeDefinition(type);
|
||||
if (sd == null) {
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN_TYPE, type);
|
||||
}
|
||||
} else {
|
||||
rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), structure != null, I18nConstants.SM_GROUP_INPUT_TYPE_NOT_DECLARED, type);
|
||||
ok = false;
|
||||
}
|
||||
if (sd != null) {
|
||||
ed = sd.getSnapshot().getElementFirstRep();
|
||||
}
|
||||
}
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), smode == null || mode.equals(smode), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode)) { // the type {0} has mode {1} which doesn't match the structure definition {2}
|
||||
v.setType(1, sd, ed, null);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,7 +455,7 @@ public class StructureMapValidator extends BaseValidator {
|
|||
List<Element> sources = rule.getChildrenByName("source");
|
||||
int cc = 0;
|
||||
for (Element source : sources) {
|
||||
ok = validateRuleSource(errors, src, group, rule, source, stack.push(source, cc, null, null), lvars, ruleInfo) && ok;
|
||||
ok = validateRuleSource(errors, src, group, rule, source, stack.push(source, cc, null, null), lvars, ruleInfo, cc) && ok;
|
||||
cc++;
|
||||
}
|
||||
// process the targets
|
||||
|
@ -431,8 +483,11 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateRuleSource(List<ValidationMessage> errors, Element src, Element group, Element rule, Element source, NodeStack stack, VariableSet variables, RuleInformation ruleInfo) {
|
||||
private boolean validateRuleSource(List<ValidationMessage> errors, Element src, Element group, Element rule, Element source, NodeStack stack, VariableSet variables, RuleInformation ruleInfo, int loopCounter) {
|
||||
String context = source.getChildValue("context");
|
||||
if (loopCounter > 0) {
|
||||
ruleInfo.setDefVariable(null);
|
||||
}
|
||||
boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(context), I18nConstants.SM_NAME_INVALID, context) &&
|
||||
rule(errors, "2023-03-01", IssueType.UNKNOWN, source.line(), source.col(), stack.getLiteralPath(), variables.hasVariable(context, SOURCE), I18nConstants.SM_SOURCE_CONTEXT_UNKNOWN, context);
|
||||
if (ok) {
|
||||
|
@ -449,6 +504,9 @@ public class StructureMapValidator extends BaseValidator {
|
|||
if (hint(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), variable != null, I18nConstants.SM_RULE_SOURCE_UNASSIGNED)) {
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(variable), I18nConstants.SM_NAME_INVALID, variable)) {
|
||||
vn = variables.add(variable, v.getMode()); // may overwrite
|
||||
if (loopCounter == 0) {
|
||||
ruleInfo.setDefVariable(variable);
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
@ -545,60 +603,82 @@ public class StructureMapValidator extends BaseValidator {
|
|||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), !els.isEmpty(), I18nConstants.SM_TARGET_PATH_INVALID, context, element, v.getEd().getPath()+"."+element)) {
|
||||
if (warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), els.size() == 1, I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) {
|
||||
ElementDefinitionSource el = els.get(0);
|
||||
String type = null; // maybe inferred / derived from transform in the future
|
||||
String transform = target.getChildValue("transform");
|
||||
List<Element> params = target.getChildren("parameter");
|
||||
if (transform == null) {
|
||||
transform = "create"; // implied
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 0, I18nConstants.SM_TARGET_NO_TRANSFORM_NO_CHECKED, transform);
|
||||
} else {
|
||||
switch (transform) {
|
||||
case "create":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() < 2, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "create", "0", "1", params.size())) {
|
||||
if (params.size() == 1) {
|
||||
type = params.get(0).primitiveValue();
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE);
|
||||
} else {
|
||||
// maybe can guess? maybe not ... type =
|
||||
}
|
||||
}
|
||||
// List<String> types = listTypes(el.getEd().getType());
|
||||
String type = null;
|
||||
if (el.getEd().getType().size() == 1) {
|
||||
type = el.getEd().getTypeFirstRep().getWorkingCode();
|
||||
} else {
|
||||
type = inferType(ruleInfo, variables, rule, transform, params);
|
||||
}
|
||||
|
||||
switch (transform) {
|
||||
case "create":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() < 2, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "create", "0", "1", params.size())) {
|
||||
if (params.size() == 1) {
|
||||
type = params.get(0).getChildValue("value");
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE);
|
||||
} else {
|
||||
ok = false;
|
||||
// maybe can guess? maybe not ... type =
|
||||
}
|
||||
break;
|
||||
case "reference":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "reference", "0", "1", params.size())) {
|
||||
type = "string";
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case "evaluate":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE, "evaluate", "1", params.size())) {
|
||||
String exp = params.get(0).primitiveValue();
|
||||
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(null, v.getSd().getType(), v.getEd().getPath(), fpe.parse(exp));
|
||||
if (td.getTypes().size() == 1) {
|
||||
type = td.getType();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_EXPRESSION_ERROR, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_NOT_CHECKED, transform);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case "copy": // logic is the same as create?
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() < 2, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "create", "0", "1", params.size())) {
|
||||
if (params.size() == 1) {
|
||||
type = params.get(0).getChildValue("value");
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE);
|
||||
} else {
|
||||
// maybe can guess? maybe not ... type =
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case "reference":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "reference", "0", "1", params.size())) {
|
||||
type = "string";
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case "evaluate":
|
||||
if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE, "evaluate", "1", params.size())) {
|
||||
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(null, v.getSd().getType(), v.getEd().getPath(), fpe.parse(exp));
|
||||
if (td.getTypes().size() == 1) {
|
||||
type = td.getType();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_EXPRESSION_ERROR, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_NOT_CHECKED, transform);
|
||||
ok = false;
|
||||
}
|
||||
//todo: transform / parameter
|
||||
if (vn != null) {
|
||||
// it's just a warning: maybe this'll work out at run time?
|
||||
warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_TARGET_TYPE_MULTIPLE_POSSIBLE, el.getEd().typeSummary());
|
||||
|
||||
vn.setType(ruleInfo.getMaxCount(), el.getSd(), el.getEd(), type); // may overwrite
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
|
@ -610,6 +690,79 @@ public class StructureMapValidator extends BaseValidator {
|
|||
return ok;
|
||||
}
|
||||
|
||||
private String inferType(RuleInformation ruleInfo, VariableSet variables, Element rule, String transform, List<Element> params) {
|
||||
// under some special conditions, we can infer what the type will be:
|
||||
// * there's a nominated default variable
|
||||
// * that variable has as single type
|
||||
// * there's a create with no param
|
||||
// * there's a single dependent rule with name = StructureMapUtilities.DEF_GROUP_NAME
|
||||
// * there's a default type group for the type of the source type
|
||||
// otherwise, we can't know the target type.
|
||||
|
||||
if (ruleInfo.getDefVariable() != null && "create".equals(transform) && params.isEmpty()) {
|
||||
VariableDefn v = variables.getVariable(ruleInfo.getDefVariable(), SOURCE);
|
||||
if (v != null && (v.getEd().getType().size() == 1 || v.getType() != null)) {
|
||||
List<Element> dependents = rule.getChildrenByName("dependent");
|
||||
if (dependents.size() == 1 && StructureMapUtilities.DEF_GROUP_NAME.equals(dependents.get(0).getChildValue("name"))) {
|
||||
String type = v.getType() != null ? v.getType() : v.getEd().getTypeFirstRep().getWorkingCode();
|
||||
// now, we look for a default group.
|
||||
// todo: look in this source
|
||||
// now look through the inputs
|
||||
for (StructureMap map : imports) {
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if (grp.getTypeMode() == StructureMapGroupTypeMode.TYPEANDTYPES && grp.getInput().size() == 2) {
|
||||
String grpType = getTypeForGroupInput(map, grp, grp.getInput().get(0));
|
||||
if (sameTypes(type, grpType)) {
|
||||
String tgtType = getTypeForGroupInput(map, grp, grp.getInput().get(1));
|
||||
if (tgtType != null) {
|
||||
return tgtType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean sameTypes(String type1, String type2) {
|
||||
if (type1 == null || type2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (!Utilities.isAbsoluteUrl(type1)) {
|
||||
type1 = "http://hl7.org/fhir/StructureDefinition/"+type1;
|
||||
}
|
||||
if (!Utilities.isAbsoluteUrl(type2)) {
|
||||
type2 = "http://hl7.org/fhir/StructureDefinition/"+type2;
|
||||
}
|
||||
return type1.equals(type2);
|
||||
}
|
||||
|
||||
private String getTypeForGroupInput(StructureMap map, StructureMapGroupComponent grp, StructureMapGroupInputComponent input) {
|
||||
String type = input.getType();
|
||||
StructureMapModelMode mode = input.getMode() == StructureMapInputMode.SOURCE ? StructureMapModelMode.SOURCE : StructureMapModelMode.TARGET;
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
for (StructureMapStructureComponent st : map.getStructure()) {
|
||||
if (type.equals(st.getAlias()) && mode == st.getMode()) {
|
||||
return st.getUrl();
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private List<String> listTypes(List<TypeRefComponent> types) {
|
||||
List<String> res = new ArrayList<>();
|
||||
for (TypeRefComponent td : types) {
|
||||
res.add(td.getWorkingCode());
|
||||
}
|
||||
Collections.sort(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private String render(List<ElementDefinitionSource> list) {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (ElementDefinitionSource t : list) {
|
||||
|
@ -668,16 +821,138 @@ public class StructureMapValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
|
||||
private boolean validateDependent(List<ValidationMessage> errors, Element src, Element group, Element dependent, NodeStack stack, VariableSet lvars) {
|
||||
private boolean validateDependent(List<ValidationMessage> errors, Element src, Element group, Element dependent, NodeStack stack, VariableSet variables) {
|
||||
boolean ok = true;
|
||||
String name = dependent.getChildValue("name");
|
||||
StructureMapGroupComponent grp = resolveGroup(name, src);
|
||||
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, dependent.line(), dependent.col(), stack.push(dependent, -1, null, null).getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, name)) {
|
||||
// check inputs
|
||||
if (StructureMapUtilities.DEF_GROUP_NAME.equals(name)) {
|
||||
VariableDefn srcVar = variables.getVariable(StructureMapUtilities.AUTO_VAR_NAME, true);
|
||||
VariableDefn tgtVar = variables.getVariable(StructureMapUtilities.AUTO_VAR_NAME, false);
|
||||
if (srcVar != null && srcVar.hasTypeInfo() && tgtVar != null && tgtVar.hasTypeInfo()) {
|
||||
String srcType = srcVar.getWorkingType();
|
||||
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;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
StructureMapGroupComponent 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())) {
|
||||
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);
|
||||
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())) {
|
||||
lvars.add(pname, v);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
cc++;
|
||||
}
|
||||
if (ok && grp.hasUserData("element.source")) {
|
||||
Element g = (Element) grp.getUserData("element.source");
|
||||
if (g.hasUserData("structuremap.parameters")) {
|
||||
throw new Error("bang! - this situation is not handled");
|
||||
} else {
|
||||
g.setUserData("structuremap.parameters", lvars);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private StructureMapGroupComponent findDefaultGroup(Element src, String srcType, String tgtType) {
|
||||
List<Element> groups = src.getChildrenByName("group");
|
||||
for (Element group : groups) {
|
||||
if (Utilities.existsInList(group.getChildValue("typeMode"), "types", "type-and-types")) {
|
||||
List<Element> inputs = group.getChildrenByName("input");
|
||||
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)) {
|
||||
return makeGroupComponent(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (StructureMap map : imports) {
|
||||
for (StructureMapGroupComponent grp : map.getGroup()) {
|
||||
if ((grp.getTypeMode() == StructureMapGroupTypeMode.TYPES || grp.getTypeMode() == StructureMapGroupTypeMode.TYPEANDTYPES) &&
|
||||
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)) {
|
||||
return grp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private String resolveInputType(StructureMap map, StructureMapGroupInputComponent input) {
|
||||
String type = input.getType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (StructureMapStructureComponent structure : map.getStructure()) {
|
||||
if (type.equals(structure.getAlias())) {
|
||||
return structure.getUrl();
|
||||
}
|
||||
}
|
||||
StructureDefinition sd = context.fetchTypeDefinition(type);
|
||||
return sd == null ? null : sd.getUrl();
|
||||
}
|
||||
|
||||
private String resolveInputType(Element src, Element input) {
|
||||
String type = input.getChildValue("type");
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (Element structure : input.getChildren("structure")) {
|
||||
if (type.equals(structure.getChildValue("alias"))) {
|
||||
return structure.getChildValue("url");
|
||||
}
|
||||
}
|
||||
StructureDefinition sd = context.fetchTypeDefinition(type);
|
||||
return sd == null ? null : sd.getUrl();
|
||||
}
|
||||
|
||||
private boolean typesMatch(VariableDefn v, String type) {
|
||||
if (type == null) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private VariableDefn getParameter(List<ValidationMessage> errors, Element param, NodeStack pstack, VariableSet variables, StructureMapInputMode mode) {
|
||||
Element v = param.getNamedChild("value");
|
||||
if (v.fhirType().equals("id")) {
|
||||
return variables.getVariable(v.primitiveValue(), mode == StructureMapInputMode.SOURCE);
|
||||
} else {
|
||||
String type = v.fhirType();
|
||||
StructureDefinition sd = context.fetchTypeDefinition(type);
|
||||
return new VariableDefn("$", "source").setType(1, sd, sd.getSnapshot().getElementFirstRep(), null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
package org.hl7.fhir.validation.instance.utils;
|
||||
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
|
||||
public class FHIRPathExpressionFixer {
|
||||
|
||||
|
||||
public static String fixExpr(String expr, String key) {
|
||||
public static String fixExpr(String expr, String key, String version) {
|
||||
// this is a hack work around for past publication of wrong FHIRPath expressions
|
||||
// R4
|
||||
// waiting for 4.0.2
|
||||
|
||||
boolean r5 = VersionUtilities.isR5Ver(version);
|
||||
// if (r5) {
|
||||
// return expr;
|
||||
// }
|
||||
|
||||
//TODO is this expression below correct? @grahamegrieve
|
||||
if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
|
||||
return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
|
||||
}
|
||||
// if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
|
||||
// return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
|
||||
// }
|
||||
if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) {
|
||||
return "enableWhen.count() >= 2 implies enableBehavior.exists()";
|
||||
}
|
||||
|
@ -52,10 +58,10 @@ public class FHIRPathExpressionFixer {
|
|||
}
|
||||
|
||||
// clarification in FHIRPath spec
|
||||
if ("eld-19".equals(key)) {
|
||||
if (!r5 && "eld-19".equals(key)) {
|
||||
return "path.matches('^[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\.[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\[x\\\\])?(\\\\:[^\\\\s\\\\.]+)?)*$')";
|
||||
}
|
||||
if ("eld-20".equals(key)) {
|
||||
if (!r5 && "eld-20".equals(key)) {
|
||||
return "path.matches('^[A-Za-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*(\\\\[x])?)*$')";
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue