Merge pull request #976 from hapifhir/gg-202211-logicals

Gg 202211 logicals
This commit is contained in:
Grahame Grieve 2022-11-03 17:14:22 +11:00 committed by GitHub
commit 23db785dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 109 additions and 43 deletions

View File

@ -691,7 +691,7 @@ public class ProfileUtilities extends TranslatingUtilities {
derived.setSnapshot(new StructureDefinitionSnapshotComponent());
try {
checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl());
checkDifferential(derived.getDifferential().getElement(), derived.getTypeName(), derived.getUrl());
checkDifferentialBaseType(derived);
copyInheritedExtensions(base, derived);
@ -712,11 +712,9 @@ public class ProfileUtilities extends TranslatingUtilities {
StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential()); // we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards
StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot();
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
String derivedType = derived.getType();
if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) {
derivedType = derivedType.substring(derivedType.lastIndexOf("/")+1);
}
baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType);
String derivedType = derived.getTypeName();
baseSnapshot = cloneSnapshot(baseSnapshot, base.getTypeName(), derivedType);
}
// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) {
// debug = true;
@ -726,7 +724,7 @@ public class ProfileUtilities extends TranslatingUtilities {
checkGroupConstraints(derived);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
if (!e.hasUserData(GENERATED_IN_SNAPSHOT) && e.getPath().contains(".")) {
ElementDefinition outcome = updateURLs(url, webUrl, e.copy());
e.setUserData(GENERATED_IN_SNAPSHOT, outcome);
derived.getSnapshot().addElement(outcome);
@ -878,11 +876,11 @@ public class ProfileUtilities extends TranslatingUtilities {
private void addInheritedElementsForSpecialization(StructureDefinitionSnapshotComponent snapshot, ElementDefinition focus, String type, String path, String url, String weburl) {
StructureDefinition sd = context.fetchTypeDefinition(type);
if (sd != null) {
addInheritedElementsForSpecialization(snapshot, focus, sd.getBaseDefinition(), path, url, weburl);
// don't do this. should already be in snapshot ... addInheritedElementsForSpecialization(snapshot, focus, sd.getBaseDefinition(), path, url, weburl);
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().contains(".")) {
ElementDefinition outcome = updateURLs(url, weburl, ed.copy());
outcome.setPath(outcome.getPath().replace(sd.getType(), path));
outcome.setPath(outcome.getPath().replace(sd.getTypeName(), path));
snapshot.getElement().add(outcome);
} else {
focus.getConstraint().addAll(ed.getConstraint());
@ -935,13 +933,6 @@ public class ProfileUtilities extends TranslatingUtilities {
return sd != null && type.size() == 1 && sd.getType().equals(type.get(0).getCode());
}
private String typeName(String type) {
if (Utilities.isAbsoluteUrl(type)) {
return type.substring(type.lastIndexOf("/")+1);
} else {
return type;
}
}
private void checkGroupConstraints(StructureDefinition derived) {
List<ElementDefinition> toRemove = new ArrayList<>();
@ -3722,7 +3713,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), tail(ed.getElement().getPath()), ed.getElement().getPath()));
} else {
c.getPieces().add(gen.new Piece(null, translate("sd.table", "See ", ed.getElement().getPath()), null));
c.getPieces().add(gen.new Piece(pfx(corePath, ed.getSource().getUserString("path"))+"#"+ed.getElement().getPath(), tail(ed.getElement().getPath())+" ("+ed.getSource().getType()+")", ed.getElement().getPath()));
c.getPieces().add(gen.new Piece(pfx(corePath, ed.getSource().getUserString("path"))+"#"+ed.getElement().getPath(), tail(ed.getElement().getPath())+" ("+ed.getSource().getTypeName()+")", ed.getElement().getPath()));
}
}
return c;
@ -3842,7 +3833,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (sd == null) {
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null)));
} else {
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), sd.getType(), null)));
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), sd.getTypeName(), null)));
}
} else if (pkp != null && pkp.hasLinkFor(tc)) {
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null)));
@ -4118,13 +4109,13 @@ public class ProfileUtilities extends TranslatingUtilities {
List<ElementDefinition> list = new ArrayList<>();
list.addAll(profile.getDifferential().getElement());
if (list.isEmpty()) {
ElementDefinition root = new ElementDefinition().setPath(profile.getType());
root.setId(profile.getType());
ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName());
root.setId(profile.getTypeName());
list.add(root);
} else {
if (list.get(0).getPath().contains(".")) {
ElementDefinition root = new ElementDefinition().setPath(profile.getType());
root.setId(profile.getType());
ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName());
root.setId(profile.getTypeName());
list.add(0, root);
}
}
@ -4652,7 +4643,7 @@ public class ProfileUtilities extends TranslatingUtilities {
choicerow.getCells().add(gen.new Cell());
choicerow.getCells().add(gen.new Cell(null, null, "", null, null));
choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE);
Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null);
Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getTypeName(), null, null);
choicerow.getCells().add(c);
if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
@ -4663,7 +4654,7 @@ public class ProfileUtilities extends TranslatingUtilities {
choicerow.getCells().add(gen.new Cell());
choicerow.getCells().add(gen.new Cell(null, null, "", null, null));
choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE);
Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null);
Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getTypeName(), null, null);
choicerow.getCells().add(c);
if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
@ -4879,7 +4870,7 @@ public class ProfileUtilities extends TranslatingUtilities {
boolean first = true;
for (StructureDefinition sd : children) {
if (first) first = false; else c.addPiece(gen.new Piece(null, ", ", null));
c.addPiece(gen.new Piece(sd.getUserString("path"), sd.getType(), null));
c.addPiece(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null));
}
}
}
@ -5052,7 +5043,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(gen.new Piece(null, type, null));
c.getPieces().add(gen.new Piece("</code>"));
} else {
c.getPieces().add(gen.new Piece(sd.getUserString("path"), sd.getType(), null));
c.getPieces().add(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null));
}
}
}
@ -5332,7 +5323,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private ElementDefinition findElementDefinition(StructureDefinition sd, String name) {
String path = sd.getType()+"."+name;
String path = sd.getTypeName()+"."+name;
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(path))
return ed;
@ -5403,7 +5394,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (ed.getSource() == profile) {
c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), "See "+ed.getElement().getPath(), null));
} else {
c.getPieces().add(gen.new Piece(ed.getSource().getUserData("path")+"#"+ed.getElement().getPath(), "See "+ed.getSource().getType()+"."+ed.getElement().getPath(), null));
c.getPieces().add(gen.new Piece(ed.getSource().getUserData("path")+"#"+ed.getElement().getPath(), "See "+ed.getSource().getTypeName()+"."+ed.getElement().getPath(), null));
}
}
}
@ -5618,7 +5609,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private String baseType(String value) {
StructureDefinition sd = context.fetchTypeDefinition(value);
if (sd != null) // might be running before all SDs are available
return sd.getType();
return sd.getTypeName();
if (Utilities.existsInList(value, "SimpleQuantity", "MoneyQuantity"))
return "Quantity";
throw new Error(context.formatMessage(I18nConstants.INTERNAL_ERROR___TYPE_NOT_KNOWN_, value));
@ -6736,7 +6727,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.setName(name);
res.setCardinality(cardinality);
res.setProfileLink(profile.getUserString("path"));
res.setResType(profile.getType());
res.setResType(profile.getTypeName());
StructureDefinition base = context.fetchResource(StructureDefinition.class, res.getResType());
if (base != null)
res.setResLink(base.getUserString("path"));
@ -7163,7 +7154,7 @@ public class ProfileUtilities extends TranslatingUtilities {
b.append("<a href=\"");
b.append(sd.getUserString("path"));
b.append("\">");
b.append(Utilities.escapeXml(sd.getType()));
b.append(Utilities.escapeXml(sd.getTypeName()));
b.append("</a>");
}
}

View File

@ -4671,7 +4671,14 @@ public String describeType() {
return "Definition";
}
}
public String getTypeName() {
String t = getType();
return StructureDefinitionKind.LOGICAL.equals(getKind()) && t.contains("/") ? t.substring(t.lastIndexOf("/")+1) : t;
}
// end addition
}

View File

@ -238,7 +238,8 @@ public class ToolingExtensions {
public static final String WEB_EXTENSION_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/branches/master/format-extensions.html#extension-related-extensions";
public static final String WEB_EXTENSION_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/format-extensions.html#extension-related-extensions";
public static final String EXT_IGDEP_COMMENT = "http://hl7.org/fhir/tools/StructureDefinition/implementationguide-dependency-comment";
;
// specific extension helpers

View File

@ -739,6 +739,15 @@ public class I18nConstants {
public static final String TYPE_SPECIFIER_NM_ILLEGAL_TYPE = "TYPE_SPECIFIER_NM_ILLEGAL_TYPE";
public static final String TYPE_SPECIFIER_NM_ABSTRACT_TYPE = "TYPE_SPECIFIER_NM_ABSTRACT_TYPE";
public static final String Bundle_BUNDLE_Entry_NO_LOGICAL_EXPL = "Bundle_BUNDLE_Entry_NO_LOGICAL_EXPL";
public static final String SD_TYPE_MISSING = "SD_TYPE_MISSING";
public static final String SD_TYPE_NOT_MATCH_NS = "SD_TYPE_NOT_MATCH_NS";
public static final String SD_TYPE_NOT_DERIVED = "SD_TYPE_NOT_DERIVED";
public static final String SD_TYPE_NOT_LOCAL = "SD_TYPE_NOT_LOCAL";
public static final String SD_TYPE_NOT_LOGICAL = "SD_TYPE_NOT_LOGICAL";
public static final String SD_CONSTRAINED_TYPE_NO_MATCH = "SD_CONSTRAINED_TYPE_NO_MATCH";
public static final String SD_SPECIALIZED_TYPE_MATCHES = "SD_SPECIALIZED_TYPE_MATCHES";
public static final String SD_CONSTRAINED_KIND_NO_MATCH = "SD_CONSTRAINED_KIND_NO_MATCH";
public static final String SD_PATH_TYPE_MISMATCH = "SD_PATH_TYPE_MISMATCH";
}

View File

@ -755,3 +755,12 @@ ELEMENT_CANNOT_BE_NULL = The element is not allowed to be 'null'
MULTIPLE_LOGICAL_MODELS_PLURAL={0} Logical Models found in supplied profiles, so unable to parse logical model (can only be one, found {1})
UNRECOGNISED_PROPERTY_TYPE = Invalid JSON type {0} for the element {1}; valid types = {2}
UNRECOGNISED_PROPERTY_TYPE_WRONG = Invalid type {2} for the element {1}; valid types = {3}, JSON type = {0}
SD_TYPE_MISSING = No type found
SD_TYPE_NOT_MATCH_NS = The type namespace {0} SHOULD match the url namespace {1} for the definition of the type
SD_TYPE_NOT_DERIVED = The type {0} can only be used as a type when constraining the base definition of the type
SD_TYPE_NOT_LOCAL = The type {0} is not legal because it is not defined in the FHIR specification. Other types must have a namespace on them
SD_TYPE_NOT_LOGICAL = The type {0} can only be defined if the kind is 'logical' not {1}
SD_CONSTRAINED_TYPE_NO_MATCH = The type {0} must be the same as the type in the base structure {1} that is being constrained
SD_SPECIALIZED_TYPE_MATCHES = The type {0} must not be the same as the type in the base structure {1} that is being specialised
SD_CONSTRAINED_KIND_NO_MATCH = The kind {0} must be the same as the kind {1} in the base structure {2}
SD_PATH_TYPE_MISMATCH = The path {1} must start with the type of the structure {0}

View File

@ -2457,7 +2457,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (context.hasBinding() && e.primitiveValue() != null) {
ok = checkPrimitiveBinding(hostContext, errors, path, type, context, e, profile, node) && ok;
// special cases
if ("StructureDefinition.type".equals(context.getPath()) && "http://hl7.org/fhir/StructureDefinition/StructureDefinition".equals(profile.getUrl())) {
ok = checkTypeValue(errors, path, e, node.getElement());
} else {
ok = checkPrimitiveBinding(hostContext, errors, path, type, context, e, profile, node) && ok;
}
}
if (type.equals("markdown") && htmlInMarkdownCheck != HtmlInMarkdownCheck.NONE) {
@ -2501,6 +2506,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok;
}
private boolean checkTypeValue(List<ValidationMessage> errors, String path, Element e, Element sd) {
String v = e.primitiveValue();
if (v == null) {
return rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.SD_TYPE_MISSING);
}
String url = sd.getChildValue("url");
String d = sd.getChildValue("derivation");
String k = sd.getChildValue("kind");
if (Utilities.isAbsoluteUrl(v)) {
warning(errors, IssueType.INVALID, e.line(), e.col(), path, ns(v).equals(ns(url)) || ns(v).equals(ns(url).replace("StructureDefinition/", "")), I18nConstants.SD_TYPE_NOT_MATCH_NS, v, url);
return rule(errors, IssueType.INVALID, e.line(), e.col(), path, "logical".equals(k), I18nConstants.SD_TYPE_NOT_LOGICAL, v, k);
} else {
boolean tok = false;
for (StructureDefinition t : context.fetchResourcesByType(StructureDefinition.class)) {
if (t.hasUserData("package") && t.getUserString("package").startsWith("hl7.fhir.r") && v.equals(t.getType())) {
tok = true;
}
}
if (tok) {
if (!(("http://hl7.org/fhir/StructureDefinition/"+v).equals(url))) {
return rule(errors, IssueType.INVALID, e.line(), e.col(), path, "constraint".equals(d), I18nConstants.SD_TYPE_NOT_DERIVED, v);
} else {
return true;
}
} else {
return rule(errors, IssueType.INVALID, e.line(), e.col(), path, tok, I18nConstants.SD_TYPE_NOT_LOCAL, v);
}
}
}
private String ns(String url) {
return url.contains("/") ? url.substring(0, url.lastIndexOf("/")) : url;
}
private Object prepWSPresentation(String s) {
if (Utilities.noString(s)) {
return "";

View File

@ -69,14 +69,18 @@ public class StructureDefinitionValidator extends BaseValidator {
public boolean validateStructureDefinition(List<ValidationMessage> errors, Element src, NodeStack stack) {
boolean ok = true;
StructureDefinition sd = null;
String typeName = null;
try {
sd = loadAsSD(src);
List<ElementDefinition> snapshot = sd.getSnapshot().getElement();
sd.setSnapshot(null);
typeName = sd.getTypeName();
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (warning(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
if (rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) {
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base.getAbstract() || sd.hasKind() && sd.getKind() == base.getKind(), I18nConstants.SD_CONSTRAINED_KIND_NO_MATCH, sd.getKind().toCode(), base.getKind().toCode(), base.getType());
if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasType() && sd.getType().equals(base.getType()), I18nConstants.SD_CONSTRAINED_TYPE_NO_MATCH, sd.getType(), base.getType());
List<ValidationMessage> msgs = new ArrayList<>();
ProfileUtilities pu = new ProfileUtilities(context, msgs, null);
pu.setXver(xverManager);
@ -100,6 +104,8 @@ public class StructureDefinitionValidator extends BaseValidator {
int is = sd.getSnapshot().getElement().size();
ok = rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is) && ok;
}
} else {
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasType() && !sd.getType().equals(base.getType()), I18nConstants.SD_SPECIALIZED_TYPE_MATCHES, sd.getType(), base.getType());
}
} else {
ok = false;
@ -116,28 +122,30 @@ public class StructureDefinitionValidator extends BaseValidator {
List<Element> differentials = src.getChildrenByName("differential");
List<Element> snapshots = src.getChildrenByName("snapshot");
for (Element differential : differentials) {
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd) && ok;
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName) && ok;
}
for (Element snapshot : snapshots) {
ok = validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true, sd) && ok;
ok = validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true, sd, typeName) && ok;
}
return ok;
}
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd) {
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName) {
boolean ok = true;
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
ok = validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd) && ok;
ok = validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName) && ok;
cc++;
}
return ok;
}
private boolean validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd) {
private boolean validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName) {
boolean ok = true;
boolean typeMustSupport = false;
String path = element.getNamedChildValue("path");
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), typeName == null || path == null || path.equals(typeName) || path.startsWith(typeName+"."), I18nConstants.SD_PATH_TYPE_MISMATCH, typeName, path);
List<Element> types = element.getChildrenByName("type");
Set<String> typeCodes = new HashSet<>();
Set<String> characteristics = new HashSet<>();
@ -173,14 +181,14 @@ public class StructureDefinitionValidator extends BaseValidator {
}
// check the stated profile - must be a constraint on the type
if (snapshot || sd != null) {
ok = validateElementType(errors, type, stack.push(type, -1, null, null), sd, element.getChildValue("path")) && ok;
ok = validateElementType(errors, type, stack.push(type, -1, null, null), sd, path) && ok;
}
}
if (typeMustSupport) {
if (snapshot) {
ok = rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, element.getNamedChildValue("path")) && ok;
ok = rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, path) && ok;
} else {
hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, element.getNamedChildValue("path"));
hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, path);
}
}
if (element.hasChild("binding")) {
@ -188,7 +196,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding", typeCodes) && ok;
}
Element binding = element.getNamedChild("binding");
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, element.getNamedChildValue("path")) && ok;
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path) && ok;
} else {
// this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
// String bt = boundType(typeCodes);
@ -375,7 +383,9 @@ public class StructureDefinitionValidator extends BaseValidator {
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path) {
boolean ok = true;
ok = rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || bindableType(typeCodes) != null, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok;
if (bindableType(typeCodes) == null) {
ok = rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok;
}
if (!snapshot) {
Set<String> bindables = getListofBindableTypes(typeCodes);
hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), bindables.size() <= 1, I18nConstants.SD_ED_BIND_MULTIPLE_TYPES, path, typeCodes.toString());