diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index 30903ddcd..41dc1bdd5 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -455,4 +455,12 @@ public class VersionUtilities { return res; } + public static String getVersionForPackage(String pid) { + if (pid.startsWith("hl7.fhir.r")) { + String[] p = pid.split("\\."); + return versionFromCode(p[2]); + } + return null; + } + } \ No newline at end of file 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 fb42ca235..81dce36fd 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 @@ -350,6 +350,7 @@ public class I18nConstants { public static final String SD_ED_TYPE_PROFILE_UNKNOWN = "SD_ED_TYPE_PROFILE_UNKNOWN"; public static final String SD_ED_TYPE_PROFILE_NOTYPE = "SD_ED_TYPE_PROFILE_NOTYPE"; public static final String SD_ED_TYPE_PROFILE_WRONG = "SD_ED_TYPE_PROFILE_WRONG"; + public static final String SD_ED_TYPE_NO_TARGET_PROFILE = "SD_ED_TYPE_NO_TARGET_PROFILE"; public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 998603a42..133ad27fd 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -618,7 +618,8 @@ SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets tha Unable_to_connect_to_terminology_server = Unable to connect to terminology server. Error = {0} SD_ED_TYPE_PROFILE_UNKNOWN = Unable to resolve profile {0} SD_ED_TYPE_PROFILE_NOTYPE = Found profile {0}, but unable to determine the type it applies it -SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but this element has type {2} +SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but the {3} element has type {2} +SD_ED_TYPE_NO_TARGET_PROFILE = Type {0} does not allow for target Profiles TERMINOLOGY_TX_NOSVC_BOUND_REQ = Could not confirm that the codes provided are from the required value set {0} because there is no terminology service TERMINOLOGY_TX_NOSVC_BOUND_EXT = Could not confirm that the codes provided are from the extensible value set {0} because there is no terminology service ARRAY_CANNOT_BE_EMPTY = Array cannot be empty - the property should not be present if it has no values diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index c26ab2078..124ade72c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -20,6 +20,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ExpressionNode; 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.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -139,12 +140,50 @@ public class StructureDefinitionValidator extends BaseValidator { } if (code != null) { List profiles = type.getChildrenByName("profile"); - for (Element profile : profiles) { - validateTypeProfile(errors, profile, code, stack.push(type, -1, null, null)); + if (VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2BVer(context.getVersion()) ) { + for (Element profile : profiles) { + validateProfileTypeOrTarget(errors, profile, code, stack.push(profile, -1, null, null), path); + } + + } else { + for (Element profile : profiles) { + validateTypeProfile(errors, profile, code, stack.push(profile, -1, null, null), path); + } + profiles = type.getChildrenByName("targetProfile"); + for (Element profile : profiles) { + validateTargetProfile(errors, profile, code, stack.push(profile, -1, null, null), path); + } } } } + private void validateProfileTypeOrTarget(List errors, Element profile, String code, NodeStack stack, String path) { + String p = profile.primitiveValue(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); + if (code.equals("Reference")) { + if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { + String t = determineBaseType(sd); + if (t == null) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + } + } + } else { + if (sd == null ) { + sd = getXverExt(errors, stack.getLiteralPath(), profile, p); + } + if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { + String t = determineBaseType(sd); + if (t == null) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + } + } + } + } + private String getTypeCodeFromSD(StructureDefinition sd, String path) { ElementDefinition ed = null; for (ElementDefinition t : sd.getSnapshot().getElement()) { @@ -159,7 +198,7 @@ public class StructureDefinitionValidator extends BaseValidator { return ed != null && ed.getType().size() == 1 ? ed.getTypeFirstRep().getCode() : null; } - private void validateTypeProfile(List errors, Element profile, String code, NodeStack stack) { + private void validateTypeProfile(List errors, Element profile, String code, NodeStack stack, String path) { String p = profile.primitiveValue(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); if (sd == null ) { @@ -170,11 +209,37 @@ public class StructureDefinitionValidator extends BaseValidator { if (t == null) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } } } + private void validateTargetProfile(List errors, Element profile, String code, NodeStack stack, String path) { + String p = profile.primitiveValue(); + StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); + if (code.equals("Reference")) { + if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { + String t = determineBaseType(sd); + if (t == null) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + } + } + } else if (code.equals("canonical")) { + if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { + String t = determineBaseType(sd); + if (t == null) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); + } + } + } else { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_NO_TARGET_PROFILE, code); + } + } + private boolean isInstanceOf(String t, String code) { StructureDefinition sd = context.fetchTypeDefinition(t); while (sd != null) {