From 4c30621491fef76bc864fa543d6f1972edefc6f2 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 27 Feb 2023 18:32:38 +1100 Subject: [PATCH] structuremap validation and invariant fixes for forthcoming R5 release --- .../loaders/loaderR5/BaseLoaderR5.java | 5 + .../org/hl7/fhir/r5/elementmodel/Element.java | 22 + .../hl7/fhir/r5/elementmodel/FmlParser.java | 12 +- .../structuremap/StructureMapUtilities.java | 1 + .../fhir/utilities/i18n/I18nConstants.java | 10 +- .../src/main/resources/Messages.properties | 16 +- .../instance/InstanceValidator.java | 10 +- .../instance/type/StructureMapValidator.java | 475 ++++++++++++++---- .../utils/FHIRPathExpressionFixer.java | 22 +- 9 files changed, 452 insertions(+), 121 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java index 3c6fa79ef..e3970607d 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java @@ -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")); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index 06306bafc..06b781f26 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java @@ -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(); @@ -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; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java index 11da04160..be7c86c70 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java @@ -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)); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java index d2ec339af..205db2d64 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java @@ -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; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 2b6f8df69..1aac697b7 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -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"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 41d8f22da..8ddd71e52 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -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} + + \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 9ac810954..96aae8e75 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -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 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(); 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); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java index 9d7620f9b..92cb3ed6f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java @@ -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 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 inputs = group.getChildrenByName("input"); + for (Element input : inputs) { + if (!input.hasChild("type")) { + return false; + } + } + return true; + } + private boolean validateImport(List 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 inputs = group.getChildrenByName("input"); List 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 inputs = group.getChildrenByName("input"); for (Element input : inputs) { @@ -352,27 +376,55 @@ public class StructureMapValidator extends BaseValidator { return grp; } - private boolean validateInput(List errors, Element src, Element group, Element input, NodeStack stack, List structures, VariableSet variables) { + private boolean validateInput(List errors, Element src, Element group, Element input, NodeStack stack, List 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 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 errors, Element src, Element group, Element rule, Element source, NodeStack stack, VariableSet variables, RuleInformation ruleInfo) { + private boolean validateRuleSource(List 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 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 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 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 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 listTypes(List types) { + List res = new ArrayList<>(); + for (TypeRefComponent td : types) { + res.add(td.getWorkingCode()); + } + Collections.sort(res); + return res; + } + private String render(List list) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (ElementDefinitionSource t : list) { @@ -668,16 +821,138 @@ public class StructureMapValidator extends BaseValidator { } - private boolean validateDependent(List errors, Element src, Element group, Element dependent, NodeStack stack, VariableSet lvars) { + private boolean validateDependent(List 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 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 groups = src.getChildrenByName("group"); + for (Element group : groups) { + if (Utilities.existsInList(group.getChildValue("typeMode"), "types", "type-and-types")) { + List 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 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); + } + } + } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java index 459f4c298..b191d0b9a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java @@ -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])?)*$')"; }