fix slicing by type and profile to allow multiple options per slice

This commit is contained in:
Grahame Grieve 2024-11-07 22:14:49 +10:30
parent afd9e7a471
commit 0c4da028b3
4 changed files with 43 additions and 29 deletions

View File

@ -612,6 +612,13 @@ public class FHIRPathEngine {
return res;
}
public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings, boolean canBeNone) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, canBeNone, expr);
warnings.addAll(typeWarnings);
return res;
}
/**
* check that paths referred to in the ExpressionNode are valid
*
@ -6585,7 +6592,7 @@ public class FHIRPathEngine {
/** given an element definition in a profile, what element contains the differentiating fixed
* for the element, given the differentiating expresssion. The expression is only allowed to
* for the element, given the differentiating expression. The expression is only allowed to
* use a subset of FHIRPath
*
* @param profile

View File

@ -178,7 +178,6 @@ public class I18nConstants {
public static final String DIFFERENTIAL_WALKS_INTO____BUT_THE_BASE_DOES_NOT_AND_THERE_IS_NOT_A_SINGLE_FIXED_TYPE_THE_TYPE_IS__THIS_IS_NOT_HANDLED_YET = "Differential_walks_into____but_the_base_does_not_and_there_is_not_a_single_fixed_type_The_type_is__This_is_not_handled_yet";
public static final String DISCRIMINATOR_BAD_PATH = "DISCRIMINATOR_BAD_PATH";
public static final String DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0 = "Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0";
public static final String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES = "Discriminator__is_based_on_type_but_slice__in__has_multiple_types";
public static final String DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES = "Discriminator__is_based_on_type_but_slice__in__has_no_types";
public static final String DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF = "Display_Name_for__should_be_one_of__instead_of";
public static final String DISPLAY_NAME_WS_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF = "Display_Name_WS_for__should_be_one_of__instead_of";
@ -475,7 +474,6 @@ public class I18nConstants {
public static final String PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__ = "Problem_processing_expression__in_profile__path__";
public static final String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_";
public static final String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_ = "Profile_based_discriminators_must_have_a_type__in_profile_";
public static final String PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE = "Profile_based_discriminators_must_have_only_one_type__in_profile";
public static final String PROFILE_EXT_NOT_HERE = "Profile_EXT_Not_Here";
public static final String PROFILE_VAL_MISSINGELEMENT = "Profile_VAL_MissingElement";
public static final String PROFILE_VAL_NOTALLOWED = "Profile_VAL_NotAllowed";
@ -1122,4 +1120,9 @@ public class I18nConstants {
public static final String CODESYSTEM_CS_COMPLETE_AND_EMPTY = "CODESYSTEM_CS_COMPLETE_AND_EMPTY";
public static final String VALIDATION_VAL_VERSION_NOHASH = "VALIDATION_VAL_VERSION_NOHASH";
public static final String PRIMITIVE_TOO_SHORT = "PRIMITIVE_TOO_SHORT";
public static final String CANONICAL_MULTIPLE_VERSIONS_KNOWN = "CANONICAL_MULTIPLE_VERSIONS_KNOWN";
public static final String SD_PATH_NO_SLICING = "SD_PATH_NO_SLICING";
public static final String SD_PATH_SLICING_DEPRECATED = "SD_PATH_SLICING_DEPRECATED";
public static final String SD_PATH_NOT_VALID = "SD_PATH_NOT_VALID";
public static final String SD_PATH_ERROR = "SD_PATH_ERROR";
}

View File

@ -179,8 +179,6 @@ Did_not_find_type_root_ = Did not find type root: {0}
Differential_does_not_have_a_slice__b_of_____in_profile_ = Differential in profile {5} does not have a slice at {6} (on {0}, position {1} of {2} / {3} / {4})
Differential_walks_into____but_the_base_does_not_and_there_is_not_a_single_fixed_type_The_type_is__This_is_not_handled_yet = Differential walks into ''{0} (@ {1})'', but the base does not, and there is not a single fixed type. The type is {2}. This is not handled yet
Discriminator__is_based_on_element_existence_but_slice__neither_sets_min1_or_max0 = Discriminator ({0}) is based on element existence, but slice {1} neither sets min>=1 or max=0
Discriminator__is_based_on_type_but_slice__in__has_multiple_types_one =
Discriminator__is_based_on_type_but_slice__in__has_multiple_types_other = Discriminator ({1}) is based on type, but slice {2} in {3} has {0} types: {4}
Discriminator__is_based_on_type_but_slice__in__has_no_types = Discriminator ({0}) is based on type, but slice {1} in {2} has no types
Display_Name_WS_for__should_be_one_of__instead_of_one = Wrong whitespace in Display Name ''{4}'' for {1}#{2}. Valid display is {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_other = Wrong whitespace in Display Name ''{4}'' for {1}#{2}. Valid display is one of {0} choices: {3} (for the language(s) ''{5}'')
@ -486,8 +484,6 @@ Profile___has_no_base_and_no_snapshot = Profile {0} ({1}) has no base and no sna
Profile__does_not_match_for__because_of_the_following_profile_issues__ = Profile {0} does not match for {1} because of the following profile issues: {2}
Profile_based_discriminators_must_have_a_type__in_profile_ = Profile based discriminators must have a type ({0} in profile {1})
Profile_based_discriminators_must_have_a_type_with_a_profile__in_profile_ = Profile based discriminators must have a type with a profile ({0} in profile {1})
Profile_based_discriminators_must_have_only_one_type__in_profile_one =
Profile_based_discriminators_must_have_only_one_type__in_profile_other = Profile based discriminators must have only one type ({1} in profile {2}) but found {0} types
QUESTIONNAIRE_QR_ITEM_BADOPTION_CS = The code provided {1} cannot be validated in the options value set ({2}) in the questionnaire because the system {0} could not be found
QUESTIONNAIRE_Q_DERIVATION_TYPE_IGNORED = The derivation type ''{0}'' means that no derivation checking has been performed against this questionnaire
QUESTIONNAIRE_Q_DERIVATION_TYPE_UNKNOWN = The derivation type ''{0}'' is unknown, which means that no derivation checking has been performed against this questionnaire
@ -1090,7 +1086,7 @@ XHTML_XHTML_NS_InValid = Wrong namespace on the XHTML (''{0}'', should be ''{1}'
XHTML_XHTML_Name_Invalid = Wrong name on the XHTML (''{0}'') - must start with div
XSI_TYPE_UNNECESSARY = xsi:type is unnecessary at this point
XSI_TYPE_WRONG = The xsi:type value ''{0}'' is wrong (should be ''{1}''). Note that xsi:type is unnecessary at this point
_DT_Fixed_Wrong = Value is ''{0}'' but must be ''{1}''{2}
_DT_Fixed_Wrong = Value is ''{0}'' but is fixed to ''{1}'' in the profile {2}
_has_children__and_multiple_types__in_profile_ = {0} has children ({1}) and multiple types ({2}) in profile {3}
_has_children__for_type__in_profile__but_cant_find_type = {0} has children ({1}) for type {2} in profile {3}, but can''t find type
_has_no_children__and_no_types_in_profile_ = {0} has no children ({1}) and no types in profile {2}
@ -1155,4 +1151,8 @@ SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE = The Usage Context value must be of
CODESYSTEM_CS_COMPLETE_AND_EMPTY = When a CodeSystem has content = ''complete'', it doesn't make sense for there to be no concepts defined
VALIDATION_VAL_VERSION_NOHASH = Version ''{0}'' contains a ''#'', which as this character is used in some URLs to separate the version and the fragment id. When version does include '#', systems will not be able to parse the URL
PRIMITIVE_TOO_SHORT = Value ''{0}'' is shorter than permitted minimum length of {1}
CANONICAL_MULTIPLE_VERSIONS_KNOWN = The version {2} for the {0} {1} is not known. These versions are known: {3}
SD_PATH_SLICING_DEPRECATED = The discriminator type ''{0}'' has been deprecated. Use type=fixed with a pattern[x] instead
SD_PATH_NOT_VALID = The discriminator path ''{0}'' does not appear to be valid for the element that is being sliced ''{1}''
SD_PATH_ERROR = The discriminator path ''{0}'' does not appear to be valid for the element that is being sliced ''{1}'': {2}

View File

@ -1047,7 +1047,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private boolean check(String v1, String v2) {
return v1 == null ? Utilities.noString(v1) : v1.equals(v2);
boolean res = v1 == null ? Utilities.noString(v1) : v1.equals(v2);
return res;
}
private boolean checkAddress(List<ValidationMessage> errors, String path, Element focus, Address fixed, String fixedSource, boolean pattern, String context) {
@ -3209,10 +3210,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (context.hasFixed()) {
ok = checkFixedValue(errors, path, e, context.getFixed(), profile.getVersionedUrl(), context.getSliceName(), null, false, "") && ok;
ok = checkFixedValue(errors, path, e, context.getFixed(), profile.getVersionedUrl(), context.getSliceName(), null, false, profile.getVersionedUrl()+"#"+context.getId()) && ok;
}
if (context.hasPattern()) {
ok = checkFixedValue(errors, path, e, context.getPattern(), profile.getVersionedUrl(), context.getSliceName(), null, true, "") && ok;
ok = checkFixedValue(errors, path, e, context.getPattern(), profile.getVersionedUrl(), context.getSliceName(), null, true, profile.getVersionedUrl()+"#"+context.getId()) && ok;
}
if (ok && !ID_EXEMPT_LIST.contains(e.fhirType())) { // ids get checked elsewhere
@ -5129,12 +5130,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if ("0".equals(criteriaElement.getMax())) {
expression.append(" and " + discriminator + ".empty()");
} else if (s.getType() == DiscriminatorType.TYPE) {
String type = null;
if (!criteriaElement.getPath().contains("[") && discriminator.contains("[")) {
discriminator = discriminator.substring(0, discriminator.indexOf('['));
String lastNode = tail(discriminator);
type = makeTypeForFHIRPath(criteriaElement.getPath()).substring(lastNode.length());
String type = makeTypeForFHIRPath(criteriaElement.getPath()).substring(lastNode.length());
expression.append(" and " + discriminator + " is " + type);
} else if (!criteriaElement.hasType() || criteriaElement.getType().size() == 1) {
String type = null;
if (discriminator.contains("["))
discriminator = discriminator.substring(0, discriminator.indexOf('['));
if (criteriaElement.hasType()) {
@ -5144,23 +5146,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else {
throw new DefinitionException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getVersionedUrl()));
}
expression.append(" and " + discriminator + " is " + type);
} else if (criteriaElement.getType().size() > 1) {
throw new DefinitionException(context.formatMessagePlural(criteriaElement.getType().size(), I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES, discriminator, ed.getId(), profile.getVersionedUrl(), criteriaElement.typeSummary()));
CommaSeparatedStringBuilder cb = new CommaSeparatedStringBuilder(" or ");
for (TypeRefComponent tr : criteriaElement.getType()) {
String type = makeTypeForFHIRPath(tr.getWorkingCode());
cb.append(discriminator + " is " + type);
}
expression.append(" and (" + cb.toString()+")");
} else
throw new DefinitionException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getVersionedUrl()));
if (discriminator.isEmpty()) {
expression.append(" and $this is " + type);
} else {
expression.append(" and " + discriminator + " is " + type);
}
} else if (s.getType() == DiscriminatorType.PROFILE) {
if (criteriaElement.getType().size() == 0) {
throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getVersionedUrl()));
}
if (criteriaElement.getType().size() != 1) {
throw new DefinitionException(context.formatMessagePlural(criteriaElement.getType().size(), I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_ONLY_ONE_TYPE__IN_PROFILE, criteriaElement.getId(), profile.getVersionedUrl()));
List<CanonicalType> list = new ArrayList<>();
boolean ref = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()");
for (TypeRefComponent tr : criteriaElement.getType()) {
list.addAll(ref ? tr.getTargetProfile() : tr.getProfile());
}
List<CanonicalType> list = discriminator.endsWith(".resolve()") || discriminator.equals("resolve()") ? criteriaElement.getType().get(0).getTargetProfile() : criteriaElement.getType().get(0).getProfile();
if (list.size() == 0) {
// we don't have to find something
// throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE_WITH_A_PROFILE__IN_PROFILE_, criteriaElement.getId(), profile.getVersionedUrl()));
@ -6357,10 +6361,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValidationInfo vi = element.addDefinition(profile, definition, mode);
if (definition.getFixed() != null) {
ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getVersionedUrl(), definition.getSliceName(), null, false, "") && ok;
ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getFixed(), profile.getVersionedUrl(), definition.getSliceName(), null, false, profile.getVersionedUrl()+"#"+definition.getId()) && ok;
}
if (definition.getPattern() != null) {
ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getPattern(), profile.getVersionedUrl(), definition.getSliceName(), null, true, "") && ok;
ok = checkFixedValue(errors, stack.getLiteralPath(), element, definition.getPattern(), profile.getVersionedUrl(), definition.getSliceName(), null, true, profile.getVersionedUrl()+"#"+definition.getId()) && ok;
}
// get the list of direct defined children, including slices
@ -6644,10 +6648,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = checkPrimitive(valContext, errors, ei.getPath(), type, checkDefn, ei.getElement(), profile, localStack, stack, valContext.getRootResource()) && ok;
} else {
if (checkDefn.hasFixed()) {
ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, false, "") && ok;
ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getFixed(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, false, profile.getVersionedUrl()+"#"+definition.getId()) && ok;
}
if (checkDefn.hasPattern()) {
ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getPattern(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, true, "") && ok;
ok = checkFixedValue(errors, ei.getPath(), ei.getElement(), checkDefn.getPattern(), profile.getVersionedUrl(), checkDefn.getSliceName(), null, true, profile.getVersionedUrl()+"#"+definition.getId()) && ok;
}
}
if (type.equals("Identifier")) {
@ -7600,7 +7604,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean valueMatchesCriteria(Element value, ElementDefinition criteria, StructureDefinition profile) throws FHIRException {
if (criteria.hasFixed()) {
List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getVersionedUrl(), "value", null, false, "");
checkFixedValue(msgs, "{virtual}", value, criteria.getFixed(), profile.getVersionedUrl(), "value", null, false, profile.getVersionedUrl()+"#"+criteria.getId());
return msgs.size() == 0;
} else if (criteria.hasBinding() && criteria.getBinding().getStrength() == BindingStrength.REQUIRED && criteria.getBinding().hasValueSet()) {
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SLICE_MATCHING__SLICE_MATCHING_BY_VALUE_SET_NOT_DONE));