Merge pull request #1368 from hapifhir/2023-07-gg-obligations-fixes

2023 07 gg obligations fixes
This commit is contained in:
Grahame Grieve 2023-07-31 08:06:39 +10:00 committed by GitHub
commit aa0aa65d61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 167 additions and 74 deletions

View File

@ -42,35 +42,60 @@ public class ICFImporter {
cs.setCopyright("© World Health Organization 2022\r\nSome rights reserved. This work is available under the Creative Commons Attribution-NoDerivatives 3.0 IGO license (CC BY-ND 3.0 IGO further specified at [[https://icd.who.int/en/docs/ICD11-license.pdf]]). \r\nUnder the terms of this license, you may copy and redistribute the work, provided the work is appropriately cited, as indicated below. In any use of this work, there should be no suggestion that WHO endorses any specific organization, products or services. The use of the WHO logo is not permitted. This license does not allow you to produce adaptations of the work (including translations) without permission from WHO.\r\nAny mediation relating to disputes arising under the license shall be conducted in accordance with the mediation rules of the World Intellectual Property Organization.\r\nThis FHIR version of ICD-11 was generated to support the FHIR Community. The definitive version of ICD-11 is available from [[https://icd.who.int/browse11/l-m/en]].\r\n");
cs.addProperty().setCode("icd11-uri").setDescription("Entity URI to map to ICD_11").setType(PropertyType.CODE);
cs.addProperty().setCode("kind").setDescription("Whether concept is chapter, block, or category").setType(PropertyType.CODE);
cs.addProperty().setCode("IsResidual").setDescription("True if the concept is not completely defined by ICD-11").setType(PropertyType.BOOLEAN);
Map<Integer, ConceptDefinitionComponent> codes = new HashMap<>();
Map<String, ConceptDefinitionComponent> codes = new HashMap<>();
int lastChapter = 0;
int lastBlock = 0;
while (csv.line()) {
String kind = csv.cell("ClassKind");
String code = csv.cell("Code");
if (!Utilities.noString(code)) {
ConceptDefinitionComponent c = new ConceptDefinitionComponent();
c.setCode(code);
c.setDisplay(fixDisplay(csv.cell("Title")));
c.addProperty().setCode("uri").setValue(new CodeType(csv.cell("Linearization (release) URI")));
String b = csv.cell("IsResidual").toLowerCase();
if (!"false".equals(b)) {
c.addProperty().setCode("IsResidual").setValue(new BooleanType(b));
if (Utilities.noString(code)) {
code = csv.cell("BlockId");
}
ConceptDefinitionComponent c = new ConceptDefinitionComponent();
c.setCode(code);
c.setDisplay(fixDisplay(csv.cell("Title")));
c.addProperty().setCode("uri").setValue(new CodeType(csv.cell("Linearization (release) URI")));
c.addProperty().setCode("kind").setValue(new CodeType(kind));
String b = csv.cell("IsResidual").toLowerCase();
if (!"false".equals(b)) {
c.addProperty().setCode("IsResidual").setValue(new BooleanType(b));
}
int level = Integer.parseInt(csv.cell("DepthInKind"));
String id = kind+"-"+level;
String parentId = null;
switch (kind) {
case "chapter":
parentId = null;
lastChapter = level;
break;
case "block":
parentId = "chapter-"+lastChapter;
lastBlock = level;
break;
case "category":
parentId = "block-"+lastBlock;
break;
}
Integer level = Integer.parseInt(csv.cell("DepthInKind"));
if (level == 1) {
cs.getConcept().add(c);
} else {
ConceptDefinitionComponent p = codes.get(level -1);
p.getConcept().add(c);
}
codes.put(level, c);
for (int i = level + 1; i < 100; i++) {
if (codes.containsKey(i)) {
codes.remove(i);
}
if (level > 1) {
parentId = kind+"-"+(level - 1);
}
System.out.println(code+" "+kind+" "+level+" "+id+" "+parentId+" ("+lastChapter+" "+lastBlock+")");
if (parentId == null) {
cs.getConcept().add(c);
} else {
ConceptDefinitionComponent p = codes.get(parentId);
p.getConcept().add(c);
}
codes.put(id, c);
for (int i = level + 1; i < 100; i++) {
if (codes.containsKey(i)) {
codes.remove(i);
}
}
}
csv.close();
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(dst), cs);

View File

@ -219,7 +219,8 @@ public class ProfileUtilities extends TranslatingUtilities {
"http://hl7.org/fhir/tools/StructureDefinition/obligation-profile",
"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status-reason",
ToolingExtensions.EXT_SUMMARY,
ToolingExtensions.EXT_OBLIGATION);
ToolingExtensions.EXT_OBLIGATION_CORE,
ToolingExtensions.EXT_OBLIGATION_TOOLS);
public IWorkerContext getContext() {
@ -2205,7 +2206,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
for (ElementDefinition ed : obligationProfileElements) {
for (Extension ext : ed.getExtension()) {
if (ToolingExtensions.EXT_OBLIGATION.equals(ext.getUrl())) {
if (Utilities.existsInList(ext.getUrl(), ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
base.getExtension().add(ext.copy());
}
}
@ -2279,13 +2280,13 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
for (Extension ext : source.getExtension()) {
if (ToolingExtensions.EXT_OBLIGATION.equals(ext.getUrl())) {
if (Utilities.existsInList(ext.getUrl(), ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
dest.getExtension().add(ext.copy());
}
}
for (ElementDefinition ed : obligationProfileElements) {
for (Extension ext : ed.getExtension()) {
if (ToolingExtensions.EXT_OBLIGATION.equals(ext.getUrl())) {
if (Utilities.existsInList(ext.getUrl(), ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
dest.getExtension().add(ext.copy());
}
}

View File

@ -41,6 +41,7 @@ import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.StandardsStatus;
import org.hl7.fhir.utilities.Utilities;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
@ -434,8 +435,14 @@ public void checkNoModifiers(String noun, String verb) throws FHIRException {
getExtension().add(ex);
}
public boolean hasExtension(String... theUrls) {
for (Extension next : getExtension()) {
if (Utilities.existsInList(next.getUrl(), theUrls)) {
return true;
}
}
return false;
}
public boolean hasExtension(String url) {
for (Extension e : getExtension())
@ -487,6 +494,18 @@ public void checkNoModifiers(String noun, String verb) throws FHIRException {
}
return Collections.unmodifiableList(retVal);
}
public List<Extension> getExtensionsByUrl(String... theUrls) {
ArrayList<Extension> retVal = new ArrayList<>();
for (Extension next : getExtension()) {
if (Utilities.existsInList(next.getUrl(), theUrls)) {
retVal.add(next);
}
}
return java.util.Collections.unmodifiableList(retVal);
}
/**
* Returns a list of modifier extensions from this element which have the given URL. Note that

View File

@ -407,6 +407,28 @@ public abstract class Element extends Base implements IBaseHasExtensions, IBaseE
return java.util.Collections.unmodifiableList(retVal);
}
public List<Extension> getExtensionsByUrl(String... theUrls) {
ArrayList<Extension> retVal = new ArrayList<>();
for (Extension next : getExtension()) {
if (Utilities.existsInList(next.getUrl(), theUrls)) {
retVal.add(next);
}
}
return java.util.Collections.unmodifiableList(retVal);
}
public boolean hasExtension(String... theUrls) {
for (Extension next : getExtension()) {
if (Utilities.existsInList(next.getUrl(), theUrls)) {
return true;
}
}
return false;
}
/**
* Returns an true if this element has an extension that matchs the given URL.
*

View File

@ -12860,7 +12860,6 @@ If a pattern[x] is declared on a repeating element, the pattern applies to all r
return t;
}
// end addition
}

View File

@ -42,11 +42,11 @@ public class ObligationsRenderer {
public static class ObligationDetail {
private String code;
private List<String> elementIds = new ArrayList<>();
private String actorId;
private String actor;
private String doco;
private String docoShort;
private String filter;
private String filterDesc;
private String filterDoco;
private List<UsageContext> usage = new ArrayList<>();
private boolean isUnchanged = false;
private boolean matched = false;
@ -58,11 +58,17 @@ public class ObligationsRenderer {
public ObligationDetail(Extension ext) {
this.code = ext.getExtensionString("code");
this.actorId = ext.getExtensionString("actorId");
this.actor = ext.getExtensionString("actor");
if (this.actor == null) {
this.actor = ext.getExtensionString("actorId");
}
this.doco = ext.getExtensionString("documentation");
this.docoShort = ext.getExtensionString("shortDoco");
this.filter = ext.getExtensionString("filter");
this.filterDesc = ext.getExtensionString("filter-desc");
this.filterDoco = ext.getExtensionString("filterDocumentation");
if (this.filterDoco == null) {
this.filterDoco = ext.getExtensionString("filter-desc");
}
for (Extension usage : ext.getExtensionsByUrl("usage")) {
this.usage.add(usage.getValueUsageContext());
}
@ -101,11 +107,11 @@ public class ObligationsRenderer {
isUnchanged = true;
isUnchanged = isUnchanged && ((code==null && compare.code==null) || code.equals(compare.code));
isUnchanged = elementIds.equals(compare.elementIds);
isUnchanged = isUnchanged && ((actorId==null && compare.actorId==null) || actorId.equals(compare.actorId));
isUnchanged = isUnchanged && ((actor==null && compare.actor==null) || actor.equals(compare.actor));
isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco));
isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort));
isUnchanged = isUnchanged && ((filter==null && compare.filter==null) || filter.equals(compare.filter));
isUnchanged = isUnchanged && ((filterDesc==null && compare.filterDesc==null) || filterDesc.equals(compare.filterDesc));
isUnchanged = isUnchanged && ((filterDoco==null && compare.filterDoco==null) || filterDoco.equals(compare.filterDoco));
isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage));
return isUnchanged;
}
@ -119,7 +125,7 @@ public class ObligationsRenderer {
}
public String getFilterDesc() {
return filterDesc;
return filterDoco;
}
public String getFilter() {
@ -131,11 +137,11 @@ public class ObligationsRenderer {
}
public boolean hasActor() {
return actorId != null;
return actor != null;
}
public boolean hasActor(String id) {
return id.equals(actorId);
return id.equals(actor);
}
}
@ -338,7 +344,7 @@ public class ObligationsRenderer {
boolean filter = false;
boolean elementId = false;
for (ObligationDetail binding : obligations) {
actor = actor || binding.actorId!=null || (binding.compare!=null && binding.compare.actorId !=null);
actor = actor || binding.actor!=null || (binding.compare!=null && binding.compare.actor !=null);
doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || !binding.usage.isEmpty() || (binding.compare!=null && !binding.compare.usage.isEmpty());
filter = filter || binding.filter != null || (binding.compare!=null && binding.compare.filter!=null);
@ -383,29 +389,29 @@ public class ObligationsRenderer {
}
if (actor) {
ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, ob.actorId);
ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, ob.actor);
ActorDefinition compAd = null;
if (ob.compare!=null && ob.compare.actorId!=null) {
compAd = context.getContext().fetchResource(ActorDefinition.class, ob.compare.actorId);
if (ob.compare!=null && ob.compare.actor!=null) {
compAd = context.getContext().fetchResource(ActorDefinition.class, ob.compare.actor);
}
XhtmlNode actorId = tr.td().style("font-size: 11px");
if (ob.compare!=null && ob.actorId.equals(ob.compare.actorId))
if (ob.compare!=null && ob.actor.equals(ob.compare.actor))
actorId.style(STYLE_UNCHANGED);
if (ad != null && ad.hasWebPath()) {
actorId.ah(ad.getWebPath(), ob.actorId).tx(ad.present());
actorId.ah(ad.getWebPath(), ob.actor).tx(ad.present());
} else if (ad != null) {
actorId.span(null, ob.actorId).tx(ad.present());
actorId.span(null, ob.actor).tx(ad.present());
}
if (ob.compare!=null && ob.compare.actorId!=null && !ob.actorId.equals(ob.compare.actorId)) {
if (ob.compare!=null && ob.compare.actor!=null && !ob.actor.equals(ob.compare.actor)) {
actorId.br();
actorId = actorId.span(STYLE_REMOVED, null);
if (compAd != null) {
if (compAd.hasWebPath()) {
actorId.ah(compAd.getWebPath(), ob.compare.actorId).tx(compAd.present());
actorId.ah(compAd.getWebPath(), ob.compare.actor).tx(compAd.present());
} else {
actorId.span(null, ob.compare.actorId).tx(compAd.present());
actorId.span(null, ob.compare.actor).tx(compAd.present());
}
}
}
@ -457,8 +463,8 @@ public class ObligationsRenderer {
if (filter) {
if (ob.filter != null) {
String d = "<code>"+ob.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.filterDesc) : "");
String oldD = ob.compare==null ? null : "<code>"+ob.compare.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.compare.filterDesc) : "");
String d = "<code>"+ob.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.filterDoco) : "");
String oldD = ob.compare==null ? null : "<code>"+ob.compare.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.compare.filterDoco) : "");
tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD));
} else {
tr.td().style("font-size: 11px");

View File

@ -500,9 +500,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
private void scanObligations(Set<String> cols, List<ElementDefinition> list, ElementDefinition ed) {
for (Extension ob : ed.getExtensionsByUrl("http://hl7.org/fhir/tools/StructureDefinition/obligation")) {
if (ob.hasExtension("actor")) {
for (Extension a : ob.getExtensionsByUrl("actor")) {
for (Extension ob : ed.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
if (ob.hasExtension("actor", "actorId")) {
for (Extension a : ob.getExtensionsByUrl("actor", "actorId")) {
cols.add(a.getValueCanonicalType().primitiveValue());
}
} else
@ -838,11 +838,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false));
}
if (element != null) {
if (element.getMustSupport() && element.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
if (element.getMustSupport() && element.hasExtension(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element has obligations and must be supported"), "SO", "white", "red", null, false));
} else if (element.getMustSupport()) {
checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false));
} else if (element != null && element.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
} else if (element != null && element.hasExtension(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element has obligations"), "O", "white", "red", null, false));
}
}
@ -1384,11 +1384,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
ObligationsRenderer obr = new ObligationsRenderer(corePath, profile, definition.getPath(), rc, null, this);
if (definition.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
obr.seeObligations(definition.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION));
if (definition.hasExtension(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
obr.seeObligations(definition.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS));
}
if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
obr.seeObligations(profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION));
if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
obr.seeObligations(profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS));
}
obr.renderTable(gen, c);

View File

@ -247,7 +247,8 @@ public class ToolingExtensions {
public static final String WEB_BINDING_STYLE = "http://build.fhir.org/ig/FHIR/fhir-tools-ig/StructureDefinition-binding-style.html";
public static final String EXT_IGDEP_COMMENT = "http://hl7.org/fhir/tools/StructureDefinition/implementationguide-dependency-comment";
public static final String EXT_XPATH_CONSTRAINT = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ElementDefinition.constraint.xpath";
public static final String EXT_OBLIGATION = "http://hl7.org/fhir/tools/StructureDefinition/obligation";
public static final String EXT_OBLIGATION_TOOLS = "http://hl7.org/fhir/tools/StructureDefinition/obligation";
public static final String EXT_OBLIGATION_CORE = "http://hl7.org/fhir/StructureDefinition/obligation";
public static final String EXT_NO_BINDING = "http://hl7.org/fhir/tools/StructureDefinition/no-binding";
;

View File

@ -112,6 +112,9 @@ public class Utilities {
return false;
}
String value = string.startsWith("-") ? string.substring(1) : string;
if (Utilities.noString(value)) {
return false;
}
for (char next : value.toCharArray()) {
if (!Character.isDigit(next)) {
return false;

View File

@ -946,6 +946,7 @@ public class I18nConstants {
public static final String ED_INVARIANT_DIFF_NO_SOURCE = "ED_INVARIANT_DIFF_NO_SOURCE";
public static final String FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT = "FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT";
public static final String FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT = "FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT";
public static final String ED_INVARIANT_KEY_ALREADY_USED = "ED_INVARIANT_KEY_ALREADY_USED";

View File

@ -970,10 +970,11 @@ QUESTIONNAIRE_Q_ITEM_DERIVED_ANSWER_OPTIONS_NEW = The item with linkId ''{1}'' f
PRIMITIVE_MUSTHAVEVALUE_MESSAGE = The element definition ``{0}`` in the profile ''{1}'' requires that a value be present in this element
PRIMITIVE_VALUE_ALTERNATIVES_MESSAGE_one = The element definition ``{0}`` in the profile ''{1}'' requires that if a value is not present, the extension ''{2}'' must be present
PRIMITIVE_VALUE_ALTERNATIVES_MESSAGE_other = The element definition ``{0}`` in the profile ''{1}'' requires that if a value is not present, one of the extensions ''{2}'' must be present
ED_INVARIANT_NO_KEY = The invariant has no key, so the content cannot be validated
ED_INVARIANT_NO_EXPRESSION = The invariant ''{0}'' has no computable expression, so validators will not be able to check it
ED_INVARIANT_EXPRESSION_CONFLICT = The invariant ''{0}'' has an expression ''{1}'', which differs from the earlier expression provided of ''{2}'' (invariants are allowed to repeat, but cannot differ)
ED_INVARIANT_EXPRESSION_ERROR = Error in invariant ''{0}'' with expression ''{1}'': {2}
ED_INVARIANT_NO_KEY = The constraint has no key, so the content cannot be validated
ED_INVARIANT_KEY_ALREADY_USED = The constraint key ''{0}'' already exists in the base profile ''{1}''
ED_INVARIANT_NO_EXPRESSION = The constraint ''{0}'' has no computable expression, so validators will not be able to check it
ED_INVARIANT_EXPRESSION_CONFLICT = The constraint ''{0}'' has an expression ''{1}'', which differs from the earlier expression provided of ''{2}'' (invariants are allowed to repeat, but cannot differ)
ED_INVARIANT_EXPRESSION_ERROR = Error in constraint ''{0}'' with expression ''{1}'': {2}
SNAPSHOT_IS_EMPTY = The snapshot for the profile ''{0}'' is empty (which should not happen)
TERMINOLOGY_TX_HINT = {1}
TERMINOLOGY_TX_WARNING = {1}

View File

@ -26,6 +26,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.ExpressionNode;
import org.hl7.fhir.r5.model.Extension;
@ -131,10 +132,10 @@ public class StructureDefinitionValidator extends BaseValidator {
boolean logical = "logical".equals(src.getNamedChildValue("kind"));
boolean constraint = "constraint".equals(src.getNamedChildValue("derivation"));
for (Element differential : differentials) {
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type"), src.getNamedChildValue("url")) && ok;
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint, src.getNamedChildValue("type"), src.getNamedChildValue("url"), base) && ok;
}
for (Element snapshotE : snapshots) {
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type"), src.getNamedChildValue("url")) && ok;
ok = validateElementList(errors, snapshotE, stack.push(snapshotE, -1, null, null), true, true, sd, typeName, logical, constraint, src.getNamedChildValue("type"), src.getNamedChildValue("url"), base) && ok;
}
// obligation profile support
@ -217,7 +218,7 @@ public class StructureDefinitionValidator extends BaseValidator {
NodeStack stack = push.push(child, c, null, null);
if (child.getName().equals("extension")) {
String url = child.getNamedChildValue("url");
if ("http://hl7.org/fhir/tools/StructureDefinition/obligation".equals(url)) {
if (Utilities.existsInList(url, ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) {
// this is ok, and it doesn't matter what's in the obligation
} else {
ok = false;
@ -332,19 +333,19 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl) {
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, String rootPath, String profileUrl, StructureDefinition base) {
Map<String, String> invariantMap = new HashMap<>();
boolean ok = true;
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl) && ok;
ok = validateElementDefinition(errors, elements, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint, invariantMap, rootPath, profileUrl, base) && ok;
cc++;
}
return ok;
}
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl) {
private boolean validateElementDefinition(List<ValidationMessage> errors, List<Element> elements, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint, Map<String, String> invariantMap, String rootPath, String profileUrl, StructureDefinition base) {
boolean ok = true;
boolean typeMustSupport = false;
String path = element.getNamedChildValue("path");
@ -472,13 +473,14 @@ public class StructureDefinitionValidator extends BaseValidator {
List<Element> constraints = element.getChildrenByName("constraint");
int cc = 0;
for (Element invariant : constraints) {
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl, snapshot) && ok;
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl, snapshot, base) && ok;
cc++;
}
return ok;
}
private boolean validateElementDefinitionInvariant(List<ValidationMessage> errors, Element invariant, NodeStack stack, Map<String, String> invariantMap, List<Element> elements, Element element, String path, String rootPath, String profileUrl, boolean snapshot) {
private boolean validateElementDefinitionInvariant(List<ValidationMessage> errors, Element invariant, NodeStack stack, Map<String, String> invariantMap, List<Element> elements, Element element,
String path, String rootPath, String profileUrl, boolean snapshot, StructureDefinition base) {
boolean ok = true;
String key = invariant.getNamedChildValue("key");
String expression = invariant.getNamedChildValue("expression");
@ -525,13 +527,26 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
} else {
ok = rule(errors, "2023-07-27", IssueType.INVALID, stack, source == null || source.equals(profileUrl), I18nConstants.ED_INVARIANT_DIFF_NO_SOURCE, key, source);
ok = rule(errors, "2023-07-27", IssueType.INVALID, stack, source == null || source.equals(profileUrl), I18nConstants.ED_INVARIANT_DIFF_NO_SOURCE, key, source) &&
rule(errors, "2023-07-27", IssueType.INVALID, stack, !haseHasInvariant(base, key), I18nConstants.ED_INVARIANT_KEY_ALREADY_USED, key, base.getVersionedUrl());
}
}
}
return ok;
}
private boolean haseHasInvariant(StructureDefinition base, String key) {
for (ElementDefinition ed : base.getSnapshot().getElement()) {
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
if (key.equals(inv.getKey())) {
return true;
}
}
}
return false;
}
private String tail(Element te, Element newte) {
String p = te.getNamedChildValue("path");
String pn = newte.getNamedChildValue("path");

View File

@ -20,7 +20,7 @@
<properties>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.3.18</validator_test_case_version>
<validator_test_case_version>1.3.19-SNAPSHOT</validator_test_case_version>
<jackson_version>2.15.2</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>