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 b88fae543..5d4ec8b84 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 @@ -787,6 +787,7 @@ public class I18nConstants { public static final String SD_NO_SLICING_ON_ROOT = "SD_NO_SLICING_ON_ROOT"; public static final String REFERENCE_REF_QUERY_INVALID = "REFERENCE_REF_QUERY_INVALID"; public static final String SM_RULEGROUP_NOT_FOUND = "SM_RULEGROUP_NOT_FOUND"; + public static final String SM_RULEGROUP_PARAM_COUNT_MISMATCH = "SM_RULEGROUP_PARAM_COUNT_MISMATCH"; public static final String SM_NAME_INVALID = "SM_NAME_INVALID"; public static final String SM_GROUP_INPUT_DUPLICATE = "SM_GROUP_INPUT_DUPLICATE"; public static final String SM_GROUP_INPUT_MODE_INVALID = "SM_GROUP_INPUT_MODE_INVALID"; @@ -862,6 +863,8 @@ public class I18nConstants { public static final String SD_NO_CONTEXT_WHEN_NOT_EXTENSION = "SD_NO_CONTEXT_WHEN_NOT_EXTENSION"; public static final String SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = "SD_CONTEXT_SHOULD_NOT_BE_ELEMENT"; public static final String SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = "SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION"; + public static final String SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE = "SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE"; + public static final String SM_TARGET_TRANSFORM_OP_INVALID_TYPE = "SM_TARGET_TRANSFORM_OP_INVALID_TYPE"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index b73b4ea97..284db76b2 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -838,6 +838,7 @@ ILLEGAL_COMMENT_TYPE = The fhir_comments property must be an array of strings SD_NO_SLICING_ON_ROOT = Slicing is not allowed at the root of a profile REFERENCE_REF_QUERY_INVALID = The query part of the conditional reference is not a valid query string ({0}) SM_RULEGROUP_NOT_FOUND = The group {0} could not be resolved +SM_RULEGROUP_PARAM_COUNT_MISMATCH = The group {0} is invoked using {1} parameters, but it actually takes {2} instead SM_NAME_INVALID = The name {0} is not valid SM_GROUP_NAME_DUPLICATE = The Group name ''{0}'' is already used SM_GROUP_INPUT_DUPLICATE = The name {0} is already used @@ -878,6 +879,8 @@ SM_MATCHING_RULEGROUP_NOT_FOUND = Unable to find a default rule for the type pai SM_TARGET_TRANSFORM_MISSING_PARAMS = One or more parameters to the translate operation are missing (should be 3, was {0}) SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM = No value for the {0} parameter found SM_TARGET_TRANSFORM_TRANSLATE_UNKNOWN_SOURCE = The source variable {0} is unknown +SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE = The {1} variable {2} is unknown for the transform {0} +SM_TARGET_TRANSFORM_OP_INVALID_TYPE = The {1} variable {0} type {2} is invalid - must be a primitive SM_TARGET_TRANSFORM_TRANSLATE_CM_NOT_FOUND = The map_uri ''{0}'' could not be resolved, so the map can''t be checked SM_TARGET_TRANSFORM_TRANSLATE_CM_BAD_MODE = The value ''{0}'' for the output parameter is incorrect SM_TARGET_TRANSLATE_BINDING_SOURCE = The source variable does not have a required binding, so this concept map can''t be checked diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index 6fafe3bea..95e22a752 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -52,6 +52,7 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; +import org.hl7.fhir.utilities.i18n.JsonLangFileProducer; import org.hl7.fhir.utilities.i18n.LanguageFileProducer.LanguageProducerLanguageSession; import org.hl7.fhir.utilities.i18n.LanguageFileProducer.LanguageProducerSession; import org.hl7.fhir.utilities.i18n.LanguageFileProducer.TranslationUnit; @@ -548,6 +549,7 @@ public class ValidationService { Utilities.createDirectory(dst); PoGetTextProducer po = new PoGetTextProducer(Utilities.path(dst)); XLIFFProducer xliff = new XLIFFProducer(Utilities.path(dst)); + JsonLangFileProducer jl = new JsonLangFileProducer(Utilities.path(dst)); List refs = new ArrayList(); ValidatorUtils.parseSources(cliContext.getSources(), refs, validator.getContext()); @@ -565,6 +567,11 @@ public class ValidationService { new LanguageUtils(validator.getContext()).generateTranslations(e, psl); psl.finish(); ps.finish(); + ps = jl.startSession(e.fhirType()+"-"+e.getIdBase(), cliContext.getSrcLang()); + psl = ps.forLang(cliContext.getTgtLang()); + new LanguageUtils(validator.getContext()).generateTranslations(e, psl); + psl.finish(); + ps.finish(); } System.out.println("Done - produced "+(po.fileCount()+xliff.fileCount()) + " files in "+dst); } 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 dd63aa7f8..4e860aca2 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 @@ -37,6 +37,7 @@ import org.hl7.fhir.r5.utils.structuremap.ResolvedGroup; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; +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; @@ -699,7 +700,7 @@ public class StructureMapValidator extends BaseValidator { if (element != null) { List els = getElementDefinitions(v.getSd(), v.getEd(), v.getType(), element); 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))) { + if (warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), els.size() == 1 || isElementandSlicing(els), I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) { ElementDefinitionSource el = els.get(0); String transform = target.getChildValue("transform"); List params = target.getChildren("parameter"); @@ -720,6 +721,16 @@ public class StructureMapValidator extends BaseValidator { 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"); + // type can be a url, a native type, or an alias + if (!Utilities.isAbsoluteUrl(type)) { + type = resolveType(type, "target", src); + if (!Utilities.isAbsoluteUrl(type)) { + StructureDefinition sdt = this.context.fetchTypeDefinition(type); + if (sdt != null) { + type = sdt.getType(); + } + } + } warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE, "create"); } else { // maybe can guess? maybe not ... type = @@ -766,6 +777,18 @@ public class StructureMapValidator extends BaseValidator { ok = false; } break; + case "cc" : + ok = rule(errors, "2023-05-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 2 || params.size() == 3, I18nConstants.SM_TARGET_TRANSFORM_MISSING_PARAMS, transform) && ok; + ok = checkParamExistsOrPrimitive(errors, params.size() > 0 ? params.get(0).getNamedChild("value") : null, "cc", "system", target, variables, stack, ok, true); + ok = checkParamExistsOrPrimitive(errors, params.size() > 1 ? params.get(1).getNamedChild("value") : null, "cc", "code", target, variables, stack, ok, true); + ok = checkParamExistsOrPrimitive(errors, params.size() > 2 ? params.get(2).getNamedChild("value") : null, "cc", "display", target, variables, stack, ok, false); + break; + case "append" : + ok = rule(errors, "2023-05-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() > 0, I18nConstants.SM_TARGET_TRANSFORM_MISSING_PARAMS, transform) && ok; + for (int i = 0; i < params.size(); i++) { + ok = checkParamExistsOrPrimitive(errors, params.get(1).getNamedChild("value"), "cc", "parameter "+i, target, variables, stack, ok, false); + } + break; case "translate": ok = rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 3, I18nConstants.SM_TARGET_TRANSFORM_MISSING_PARAMS, transform) && ok; Element srcE = params.size() > 0 ? params.get(0).getNamedChild("value") : null; @@ -829,6 +852,36 @@ public class StructureMapValidator extends BaseValidator { } + private boolean checkParamExistsOrPrimitive(List errors, Element e, String string, String string2, Element target, VariableSet variables, NodeStack stack, boolean ok, boolean mandatory) { + if (!mandatory && e == null) { + return ok; + } else if (rule(errors, "2023-05-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), e != null, I18nConstants.SM_TARGET_TRANSFORM_TRANSLATE_NO_PARAM, "system")) { + if ("id".equals(e.fhirType())) { + VariableDefn sv = variables.getVariable(e.getValue(), true); + rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), e != null, I18nConstants.SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE, "cc", "system", e.getValue()); + } else { + rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), e.isPrimitive(), I18nConstants.SM_TARGET_TRANSFORM_OP_INVALID_TYPE, "cc", "system",e.fhirType()); + } + return ok; + } else { + return false; + } + + } + + private boolean isElementandSlicing(List els) { + if (els.size()== 0) { + return false; + } + String path = els.get(0).getEd().getPath(); + for (int i = 1; i < els.size(); i++ ) { + if (!els.get(i).getEd().hasSliceName() || !els.get(i).getEd().getPath().equals(path)) { + return false; + } + } + return true; + } + private boolean checkConceptMap(List errors, int line, int col, String literalPath, ConceptMap cm, ElementDefinition srcED, ElementDefinition tgtED) { boolean ok = true; ValueSet srcVS = null; @@ -1077,8 +1130,8 @@ public class StructureMapValidator extends BaseValidator { } else { ResolvedGroup grp = resolveGroup(name, src); if (rule(errors, "2023-03-01", IssueType.NOTFOUND, dependent.line(), dependent.col(), stack.getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, name)) { - List params = dependent.getChildren("parameter"); - if (rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), params.size() == grp.getTargetGroup().getInput().size(), I18nConstants.SM_RULEGROUP_NOT_FOUND, params.size(), grp.getTargetGroup().getInput().size())) { + List params = dependent.getChildren(VersionUtilities.isR5Plus(context.getVersion()) ? "parameter" : "variable"); + if (rule(errors, "2023-03-01", IssueType.INVALID, dependent.line(), dependent.col(), stack.getLiteralPath(), params.size() == grp.getTargetGroup().getInput().size(), I18nConstants.SM_RULEGROUP_PARAM_COUNT_MISMATCH, name, params.size(), grp.getTargetGroup().getInput().size())) { VariableSet lvars = new VariableSet(); int cc = 0; for (Element param : params) { @@ -1136,6 +1189,18 @@ public class StructureMapValidator extends BaseValidator { return input.getType(); } + private String resolveType(String type, String mode, Element map) { + List structures = map.getChildrenByName("structure"); + for (Element structure : structures) { + String alias = structure.getChildValue("alias"); + if ((alias != null && alias.equals(type)) && (mode == null || mode.equals(structure.getNamedChildValue("mode")))) { + return structure.getChildValue("url"); + } + } + + return type; + } + private StructureMapGroupComponent findDefaultGroup(Element src, String srcType, String tgtType) { List groups = src.getChildrenByName("group"); for (Element group : groups) { @@ -1199,24 +1264,38 @@ public class StructureMapValidator extends BaseValidator { return true; } else if (v.getSd().getUrl().equals(type) || v.getSd().getType().equals(type)) { return true; + } else if (v.getType() != null && v.getType().equals(type)) { + return true; } else { for (TypeRefComponent tr : v.getEd().getType()) { if (type.equals(tr.getWorkingCode()) || type.equals("http://hl7.org/fhir/StructureDefinition/"+tr.getWorkingCode())) { return true; } } + StructureDefinition tsd = context.fetchTypeDefinition(type); + StructureDefinition sd = context.fetchTypeDefinition(v.getType()); + while (sd != null) { + if (sd.getUrl().equals(tsd.getUrl())) { + return true; + } + sd = context.fetchTypeDefinition(sd.getBaseDefinition()); + } 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); + if (VersionUtilities.isR5Plus(context.getVersion())) { + 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); + } } else { - String type = v.fhirType(); - StructureDefinition sd = context.fetchTypeDefinition(type); - return new VariableDefn("$", "source").setType(1, sd, sd.getSnapshot().getElementFirstRep(), null); + return variables.getVariable(param.primitiveValue(), mode == StructureMapInputMode.SOURCE); } }