org.hl7.fhir.r5.comparison.ValueSetComparer Maven / Gradle / Ivy
package org.hl7.fhir.r5.comparison;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.Element;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.CodeSystemRenderer;
import org.hl7.fhir.r5.renderers.ValueSetRenderer;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.RenderingI18nContext;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@MarkedToMoveToAdjunctPackage
public class ValueSetComparer extends CanonicalResourceComparer {
public class ValueSetComparison extends CanonicalResourceComparison {
public ValueSetComparison(ValueSet left, ValueSet right) {
super(left, right);
}
private StructuralMatch includes = new StructuralMatch<>();
private StructuralMatch excludes = new StructuralMatch<>();
private StructuralMatch expansion;
public StructuralMatch getIncludes() {
return includes;
}
public StructuralMatch getExcludes() {
return excludes;
}
public StructuralMatch getExpansion() {
return expansion;
}
public StructuralMatch forceExpansion() {
if (expansion == null) {
expansion = new StructuralMatch<>();
}
return expansion;
}
@Override
protected String abbreviation() {
return "vs";
}
@Override
protected String summary() {
String res = "ValueSet: "+left.present()+" vs "+right.present();
String ch = changeSummary();
if (ch != null) {
res = res + ". "+ch;
}
return res;
}
@Override
protected String fhirType() {
return "ValueSet";
}
@Override
protected void countMessages(MessageCounts cnts) {
super.countMessages(cnts);
if (includes != null) {
includes.countMessages(cnts);
}
if (excludes != null) {
excludes.countMessages(cnts);
}
if (expansion != null) {
expansion.countMessages(cnts);
}
}
}
public ValueSetComparer(ComparisonSession session) {
super(session);
}
public ValueSetComparison compare(ValueSet left, ValueSet right) {
if (left == null)
throw new DefinitionException("No ValueSet provided (left)");
if (right == null)
throw new DefinitionException("No ValueSet provided (right)");
ValueSetComparison res = new ValueSetComparison(left, right);
session.identify(res);
ValueSet vs = new ValueSet();
res.setUnion(vs);
session.identify(vs);
vs.setName("Union"+left.getName()+"And"+right.getName());
vs.setTitle("Union of "+left.getTitle()+" And "+right.getTitle());
vs.setStatus(left.getStatus());
vs.setDate(new Date());
ValueSet vs1 = new ValueSet();
res.setIntersection(vs1);
session.identify(vs1);
vs1.setName("Intersection"+left.getName()+"And"+right.getName());
vs1.setTitle("Intersection of "+left.getTitle()+" And "+right.getTitle());
vs1.setStatus(left.getStatus());
vs1.setDate(new Date());
List chMetadata = new ArrayList<>();
var ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata, right);
var def = false;
if (comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("immutable");
}
if (left.hasCompose() || right.hasCompose()) {
if (comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("compose.lockedDate");
}
def = comparePrimitives("compose.inactive", left.getCompose().getInactiveElement(), right.getCompose().getInactiveElement(), res.getMetadata(), IssueSeverity.WARNING, res) || def;
}
res.updatedMetadataState(ch, chMetadata);
def = compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()) || def;
res.updateDefinitionsState(def);
// compareExpansions(left, right, res);
session.annotate(right, res);
return res;
}
private boolean compareCompose(ValueSetComposeComponent left, ValueSetComposeComponent right, ValueSetComparison res, ValueSetComposeComponent union, ValueSetComposeComponent intersection) {
boolean def = false;
// first, the includes
List matchR = new ArrayList<>();
for (ConceptSetComponent l : left.getInclude()) {
ConceptSetComponent r = findInList(right.getInclude(), l, left.getInclude());
if (r == null) {
union.getInclude().add(l);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed Include", "ValueSet.compose.include")));
session.markDeleted(right, "include", l);
} else {
matchR.add(r);
ConceptSetComponent csM = new ConceptSetComponent();
ConceptSetComponent csI = new ConceptSetComponent();
union.getInclude().add(csM);
intersection.getInclude().add(csI);
StructuralMatch sm = new StructuralMatch(l, r);
res.getIncludes().getChildren().add(sm);
def = compareDefinitions("ValueSet.compose.include["+right.getInclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def;
}
}
for (ConceptSetComponent r : right.getInclude()) {
if (!matchR.contains(r)) {
union.getInclude().add(r);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added Include", "ValueSet.compose.include"), r));
session.markAdded(r);
}
}
// now. the excludes
matchR.clear();
for (ConceptSetComponent l : left.getExclude()) {
ConceptSetComponent r = findInList(right.getExclude(), l, left.getExclude());
if (r == null) {
union.getExclude().add(l);
res.updateContentState(true);
res.getExcludes().getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed Exclude", "ValueSet.compose.exclude")));
} else {
matchR.add(r);
ConceptSetComponent csM = new ConceptSetComponent();
ConceptSetComponent csI = new ConceptSetComponent();
union.getExclude().add(csM);
intersection.getExclude().add(csI);
StructuralMatch sm = new StructuralMatch(l, r);
res.getExcludes().getChildren().add(sm);
def = compareDefinitions("ValueSet.compose.exclude["+right.getExclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def;
}
}
for (ConceptSetComponent r : right.getExclude()) {
if (!matchR.contains(r)) {
union.getExclude().add(r);
res.updateContentState(true);
res.getExcludes().getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added Exclude", "ValueSet.compose.exclude"), r));
}
}
return def;
}
private ConceptSetComponent findInList(List matches, ConceptSetComponent item, List source) {
if (matches.size() == 1 && source.size() == 1) {
return matches.get(0);
}
int matchCount = countMatchesBySystem(matches, item);
int sourceCount = countMatchesBySystem(source, item);
if (matchCount == 1 && sourceCount == 1) {
for (ConceptSetComponent t : matches) {
if (t.getSystem() != null && t.getSystem().equals(item.getSystem())) {
return t;
}
}
}
// if there's more than one candidate match by system, then we look for a full match
for (ConceptSetComponent t : matches) {
if (t.equalsDeep(item)) {
return t;
}
}
return null;
}
private int countMatchesBySystem(List list, ConceptSetComponent item) {
int c = 0;
for (ConceptSetComponent t : list) {
if (t.hasSystem() && t.getSystem().equals(item.getSystem())) {
c++;
}
}
return c;
}
private boolean compareDefinitions(String path, ConceptSetComponent left, ConceptSetComponent right, StructuralMatch combined, ConceptSetComponent union, ConceptSetComponent intersection, ValueSetComparison res) {
boolean def = false;
// system must match, but the rest might not. we're going to do the full comparison whatever, so the outcome looks consistent to the user
List matchVSR = new ArrayList<>();
for (CanonicalType l : left.getValueSet()) {
CanonicalType r = findInList(right.getValueSet(), l, left.getValueSet());
if (r == null) {
union.getValueSet().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.ERROR, "Removed ValueSet", "ValueSet.compose.include.valueSet")));
if (session.isAnnotate()) {
session.markDeleted(right, "valueset", l);
}
} else {
matchVSR.add(r);
if (l.getValue().equals(r.getValue())) {
union.getValueSet().add(l);
intersection.getValueSet().add(l);
StructuralMatch sm = new StructuralMatch(l, r, null);
combined.getChildren().add(sm);
} else {
// it's not possible to get here?
union.getValueSet().add(l);
union.getValueSet().add(r);
res.updateContentState(true);
StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.WARNING, "Values are different", "ValueSet.compose.include.valueSet"));
combined.getChildren().add(sm);
if (session.isAnnotate()) {
session.markChanged(r, l);
}
}
}
}
for (CanonicalType r : right.getValueSet()) {
if (!matchVSR.contains(r)) {
union.getValueSet().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.ERROR, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
session.markAdded(r);
}
}
List matchCR = new ArrayList<>();
for (ConceptReferenceComponent l : left.getConcept()) {
ConceptReferenceComponent r = findInList(right.getConcept(), l, left.getConcept());
if (r == null) {
union.getConcept().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.ERROR, "Removed this Concept", "ValueSet.compose.include.concept")));
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+l.getCode()+" removed", IssueSeverity.ERROR));
session.markDeleted(right,"concept", l);
} else {
matchCR.add(r);
if (l.getCode().equals(r.getCode())) {
ConceptReferenceComponent cu = new ConceptReferenceComponent();
ConceptReferenceComponent ci = new ConceptReferenceComponent();
union.getConcept().add(cu);
intersection.getConcept().add(ci);
StructuralMatch sm = new StructuralMatch(l, r);
combined.getChildren().add(sm);
def = compareConcepts(path+".concept["+right.getConcept().indexOf(r)+"]", l, r, sm, cu, ci, res) || def;
} else {
// not that it's possible to get here?
union.getConcept().add(l);
union.getConcept().add(r);
StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.WARNING, "Concepts are different", "ValueSet.compose.include.concept"));
combined.getChildren().add(sm);
res.updateContentState(true);
compareConcepts(path+".concept["+right.getConcept().indexOf(r)+"]", l, r, sm, null, null, res);
session.markChanged(r, l);
}
}
}
for (ConceptReferenceComponent r : right.getConcept()) {
if (!matchCR.contains(r)) {
union.getConcept().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.ERROR, "Added this Concept", "ValueSet.compose.include.concept"), r));
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+r.getCode()+" added", IssueSeverity.ERROR));
session.markAdded(r);
}
}
List matchFR = new ArrayList<>();
for (ConceptSetFilterComponent l : left.getFilter()) {
ConceptSetFilterComponent r = findInList(right.getFilter(), l, left.getFilter());
if (r == null) {
union.getFilter().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.ERROR, "Removed this item", "ValueSet.compose.include.filter")));
session.markDeleted(right, "filter", l);
} else {
matchFR.add(r);
if (l.getProperty().equals(r.getProperty()) && l.getOp().equals(r.getOp())) {
ConceptSetFilterComponent cu = new ConceptSetFilterComponent();
ConceptSetFilterComponent ci = new ConceptSetFilterComponent();
union.getFilter().add(cu);
intersection.getFilter().add(ci);
StructuralMatch sm = new StructuralMatch(l, r);
combined.getChildren().add(sm);
if (compareFilters(l, r, sm, cu, ci)) {
res.updateContentState(true);
session.markChanged(r, l);
}
} else {
union.getFilter().add(l);
union.getFilter().add(r);
StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.WARNING, "Codes are different", "ValueSet.compose.include.filter"));
res.updateContentState(true);
combined.getChildren().add(sm);
compareFilters(l, r, sm, null, null);
}
}
}
for (ConceptSetFilterComponent r : right.getFilter()) {
if (!matchFR.contains(r)) {
union.getFilter().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.ERROR, "Added this item", "ValueSet.compose.include.filter"), r));
session.markAdded(r);
}
}
return def;
}
private boolean compareConcepts(String path, ConceptReferenceComponent l, ConceptReferenceComponent r, StructuralMatch sm, ConceptReferenceComponent cu, ConceptReferenceComponent ci, ValueSetComparison res) {
boolean def = false;
sm.getChildren().add(new StructuralMatch(l.getCodeElement(), r.getCodeElement(), l.getCode().equals(r.getCode()) ? null : vmI(IssueSeverity.INFORMATION, "Codes do not match", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setCode(l.getCode());
cu.setCode(l.getCode());
}
if (l.hasDisplay() && r.hasDisplay()) {
sm.getChildren().add(new StructuralMatch(l.getDisplayElement(), r.getDisplayElement(), l.getDisplay().equals(r.getDisplay()) ? null : vmI(IssueSeverity.INFORMATION, "Displays do not match", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setDisplay(r.getDisplay());
cu.setDisplay(r.getDisplay());
}
def = !l.getDisplay().equals(r.getDisplay());
if (def) {
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+l.getCode()+" display changed from '"+l.getDisplay()+"' to '"+r.getDisplay()+"'", IssueSeverity.WARNING));
session.markChanged(r.getDisplayElement(), l.getDisplayElement());
}
} else if (l.hasDisplay()) {
session.markDeleted(r, "display", l.getDisplayElement());
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+l.getCode()+" display '"+l.getDisplay()+"' removed", IssueSeverity.WARNING));
sm.getChildren().add(new StructuralMatch(l.getDisplayElement(), null, vmI(IssueSeverity.INFORMATION, "Display Removed", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setDisplay(l.getDisplay());
cu.setDisplay(l.getDisplay());
}
def = true;
} else if (r.hasDisplay()) {
session.markAdded(r.getDisplayElement());
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+l.getCode()+" display '"+r.getDisplay()+"' added", IssueSeverity.WARNING));
sm.getChildren().add(new StructuralMatch(null, r.getDisplayElement(), vmI(IssueSeverity.INFORMATION, "Display added", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setDisplay(r.getDisplay());
cu.setDisplay(r.getDisplay());
}
def = true;
} else {
sm.getChildren().add(new StructuralMatch(null, null, vmI(IssueSeverity.INFORMATION, "No Display", "ValueSet.compose.include.concept")));
}
return def;
}
private boolean compareFilters(ConceptSetFilterComponent l, ConceptSetFilterComponent r, StructuralMatch sm, ConceptSetFilterComponent cu, ConceptSetFilterComponent ci) {
sm.getChildren().add(new StructuralMatch(l.getPropertyElement(), r.getPropertyElement(), l.getProperty().equals(r.getProperty()) ? null : vmI(IssueSeverity.INFORMATION, "Properties do not match", "ValueSet.compose.include.concept")));
sm.getChildren().add(new StructuralMatch(l.getOpElement(), r.getOpElement(), l.getOp().equals(r.getOp()) ? null : vmI(IssueSeverity.INFORMATION, "Filter Operations do not match", "ValueSet.compose.include.concept")));
sm.getChildren().add(new StructuralMatch(l.getValueElement(), r.getValueElement(), l.getValue().equals(r.getValue()) ? null : vmI(IssueSeverity.INFORMATION, "Values do not match", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setProperty(l.getProperty());
ci.setOp(l.getOp());
ci.setValue(l.getValue());
cu.setProperty(l.getProperty());
cu.setOp(l.getOp());
cu.setValue(l.getValue());
}
return !l.getValue().equals(r.getValue());
}
private CanonicalType findInList(List matches, CanonicalType item, List source) {
if (matches.size() == 1 && source.size() == 1) {
return matches.get(0);
}
for (CanonicalType t : matches) {
if (t.getValue().equals(item.getValue())) {
return t;
}
}
return null;
}
private ConceptReferenceComponent findInList(List matches, ConceptReferenceComponent item, List source) {
if (matches.size() == 1 && source.size() == 1) {
return matches.get(0);
}
for (ConceptReferenceComponent t : matches) {
if (t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private ConceptSetFilterComponent findInList(List matches, ConceptSetFilterComponent item, List source) {
if (matches.size() == 1 && source.size() == 1) {
return matches.get(0);
}
for (ConceptSetFilterComponent t : matches) {
if (t.getProperty().equals(item.getProperty()) && t.getOp().equals(item.getOp()) ) {
return t;
}
}
return null;
}
private void compareExpansions(ValueSet left, ValueSet right, ValueSetComparison res) {
ValueSet expL = left.hasExpansion() ? left : expand(left, res, "left", session.getContextLeft());
ValueSet expR = right.hasExpansion() ? right : expand(right, res, "right", session.getContextRight());
if (expL != null && expR != null) {
// ignore the parameters for now
compareConcepts(expL.getExpansion().getContains(), expR.getExpansion().getContains(), res.forceExpansion(), res.getUnion().getExpansion().getContains(), res.getIntersection().getExpansion().getContains(), "ValueSet.expansion.contains", res);
}
}
private ValueSet expand(ValueSet vs, ValueSetComparison res, String name, IWorkerContext ctxt) {
ValueSetExpansionOutcome vse = ctxt.expandVS(vs, true, false);
if (vse.getValueset() != null) {
return vse.getValueset();
} else {
res.getMessages().add(new ValidationMessage(Source.TerminologyEngine, IssueType.EXCEPTION, "ValueSet", "Error Expanding "+name+":"+vse.getError(), IssueSeverity.ERROR));
return null;
}
}
private void compareConcepts(List left, List right, StructuralMatch combined, List union, List intersection, String path, ValueSetComparison res) {
List matchR = new ArrayList<>();
for (ValueSetExpansionContainsComponent l : left) {
ValueSetExpansionContainsComponent r = findInList(right, l);
if (r == null) {
union.add(l);
combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed from expansion", path)));
} else {
matchR.add(r);
ValueSetExpansionContainsComponent ccU = merge(l, r);
ValueSetExpansionContainsComponent ccI = intersect(l, r);
union.add(ccU);
intersection.add(ccI);
StructuralMatch sm = new StructuralMatch(l, r);
compareItem(sm.getMessages(), path, l, r, res);
combined.getChildren().add(sm);
compareConcepts(l.getContains(), r.getContains(), sm, ccU.getContains(), ccI.getContains(), path+".where(code = '"+l.getCode()+"').contains", res);
}
}
for (ValueSetExpansionContainsComponent r : right) {
if (!matchR.contains(r)) {
union.add(r);
combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added to expansion", path), r));
}
}
}
private void compareItem(List msgs, String path, ValueSetExpansionContainsComponent l, ValueSetExpansionContainsComponent r, ValueSetComparison res) {
compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res);
}
private void compareStrings(String path, List msgs, String left, String right, String name, IssueSeverity level, ValueSetComparison res) {
if (!Utilities.noString(right)) {
if (Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" added", path));
} else if (!left.equals(right)) {
if (level != IssueSeverity.NULL) {
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+".name", "Changed value for "+name+": '"+left+"' vs '"+right+"'", level));
}
msgs.add(vmI(level, name+" changed from left to right", path));
}
} else if (!Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" removed", path));
}
}
private ValueSetExpansionContainsComponent findInList(List list, ValueSetExpansionContainsComponent item) {
for (ValueSetExpansionContainsComponent t : list) {
if (t.getSystem().equals(item.getSystem()) && t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private ValueSetExpansionContainsComponent intersect(ValueSetExpansionContainsComponent l, ValueSetExpansionContainsComponent r) {
ValueSetExpansionContainsComponent res = new ValueSetExpansionContainsComponent();
if (l.hasAbstract() && r.hasAbstract()) {
res.setAbstract(l.getAbstract());
}
if (l.hasCode() && r.hasCode()) {
res.setCode(l.getCode());
}
if (l.hasSystem() && r.hasSystem()) {
res.setSystem(l.getSystem());
}
if (l.hasVersion() && r.hasVersion()) {
res.setVersion(l.getVersion());
}
if (l.hasDisplay() && r.hasDisplay()) {
res.setDisplay(l.getDisplay());
}
return res;
}
private ValueSetExpansionContainsComponent merge(ValueSetExpansionContainsComponent l, ValueSetExpansionContainsComponent r) {
ValueSetExpansionContainsComponent res = new ValueSetExpansionContainsComponent();
if (l.hasAbstract()) {
res.setAbstract(l.getAbstract());
} else if (r.hasAbstract()) {
res.setAbstract(r.getAbstract());
}
if (l.hasCode()) {
res.setCode(l.getCode());
} else if (r.hasCode()) {
res.setCode(r.getCode());
}
if (l.hasSystem()) {
res.setSystem(l.getSystem());
} else if (r.hasSystem()) {
res.setSystem(r.getSystem());
}
if (l.hasVersion()) {
res.setVersion(l.getVersion());
} else if (r.hasVersion()) {
res.setVersion(r.getVersion());
}
if (l.hasDisplay()) {
res.setDisplay(l.getDisplay());
} else if (r.hasDisplay()) {
res.setDisplay(r.getDisplay());
}
return res;
}
@Override
protected String fhirType() {
return "ValueSet";
}
public XhtmlNode renderCompose(ValueSetComparison csc, String id, String prefix) throws FHIRException, IOException {
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(new RenderingI18nContext(), Utilities.path("[tmp]", "comparison"), false, "c");
TableModel model = gen.new TableModel(id, true);
model.setAlternating(true);
model.getTitles().add(gen.new Title(null, null, "Item", "The type of item being compared", null, 100));
model.getTitles().add(gen.new Title(null, null, "Property", "The system for the concept", null, 100, 2));
model.getTitles().add(gen.new Title(null, null, "Value", "The display for the concept", null, 200, 2));
model.getTitles().add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200));
for (StructuralMatch t : csc.getIncludes().getChildren()) {
addComposeRow(gen, model.getRows(), t, "include");
}
for (StructuralMatch t : csc.getExcludes().getChildren()) {
addComposeRow(gen, model.getRows(), t, "exclude");
}
return gen.generate(model, prefix, 0, null);
}
private void addComposeRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, String name) {
Row r = gen.new Row();
rows.add(r);
r.getCells().add(gen.new Cell(null, null, name, null, null));
if (t.hasLeft() && t.hasRight()) {
ConceptSetComponent csL = (ConceptSetComponent) t.getLeft();
ConceptSetComponent csR = (ConceptSetComponent) t.getRight();
if (csL.hasSystem() && csL.getSystem().equals(csR.getSystem())) {
r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
r.getCells().add(gen.new Cell(null, null, csR.getSystem(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
}
if (csL.hasVersion() && csR.hasVersion()) {
if (csL.getVersion().equals(csR.getVersion())) {
r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
r.getCells().add(gen.new Cell(null, null, csR.getVersion(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
}
} else if (csL.hasVersion()) {
r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null));
r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));
} else if (csR.hasVersion()) {
r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));
r.getCells().add(gen.new Cell(null, null, csR.getVersion(), null, null));
} else {
r.getCells().add(missingCell(gen).span(2).center());
}
} else if (t.hasLeft()) {
r.setColor(COLOR_NO_ROW_RIGHT);
ConceptSetComponent cs = (ConceptSetComponent) t.getLeft();
r.getCells().add(gen.new Cell(null, null, cs.getSystem(), null, null));
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.hasVersion() ? "Version: "+cs.getVersion() : "", null, null));
r.getCells().add(missingCell(gen));
} else {
r.setColor(COLOR_NO_ROW_LEFT);
ConceptSetComponent cs = (ConceptSetComponent) t.getRight();
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.getSystem(), null, null));
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.hasVersion() ? "Version: "+cs.getVersion() : "", null, null));
}
r.getCells().add(cellForMessages(gen, t.getMessages()));
for (StructuralMatch c : t.getChildren()) {
if (c.either() instanceof ConceptReferenceComponent) {
addSetConceptRow(gen, r.getSubRows(), c);
} else {
addSetFilterRow(gen, r.getSubRows(), c);
}
}
}
private void addSetConceptRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t) {
Row r = gen.new Row();
rows.add(r);
r.getCells().add(gen.new Cell(null, null, "Concept", null, null));
if (t.hasLeft() && t.hasRight()) {
ConceptReferenceComponent csL = (ConceptReferenceComponent) t.getLeft();
ConceptReferenceComponent csR = (ConceptReferenceComponent) t.getRight();
// we assume both have codes
if (csL.getCode().equals(csR.getCode())) {
r.getCells().add(gen.new Cell(null, null, csL.getCode(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, csL.getCode(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
r.getCells().add(gen.new Cell(null, null, csR.getCode(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
}
if (csL.hasDisplay() && csR.hasDisplay()) {
if (csL.getDisplay().equals(csR.getDisplay())) {
r.getCells().add(gen.new Cell(null, null, csL.getDisplay(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, csL.getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
r.getCells().add(gen.new Cell(null, null, csR.getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
}
} else if (csL.hasDisplay()) {
r.getCells().add(gen.new Cell(null, null, csL.getDisplay(), null, null));
r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));
} else if (csR.hasDisplay()) {
r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));
r.getCells().add(gen.new Cell(null, null, csR.getDisplay(), null, null));
} else {
r.getCells().add(missingCell(gen).span(2).center());
}
} else if (t.hasLeft()) {
r.setColor(COLOR_NO_ROW_RIGHT);
ConceptReferenceComponent cs = (ConceptReferenceComponent) t.getLeft();
r.getCells().add(gen.new Cell(null, null, cs.getCode(), null, null));
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.hasDisplay() ? "Version: "+cs.getDisplay() : "", null, null));
r.getCells().add(missingCell(gen));
} else {
r.setColor(COLOR_NO_ROW_LEFT);
ConceptReferenceComponent cs = (ConceptReferenceComponent) t.getRight();
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.getCode(), null, null));
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, cs.hasDisplay() ? "Version: "+cs.getDisplay() : "", null, null));
}
r.getCells().add(cellForMessages(gen, t.getMessages()));
}
private void addSetFilterRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t) {
// Row r = gen.new Row();
// rows.add(r);
// r.getCells().add(gen.new Cell(null, null, "Filter", null, null));
// if (t.hasLeft() && t.hasRight()) {
// ConceptSetComponent csL = (ConceptSetComponent) t.getLeft();
// ConceptSetComponent csR = (ConceptSetComponent) t.getRight();
// // we assume both have systems
// if (csL.getSystem().equals(csR.getSystem())) {
// r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).span(2).center());
// } else {
// r.getCells().add(gen.new Cell(null, null, csL.getSystem(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
// r.getCells().add(gen.new Cell(null, null, csR.getSystem(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
// }
//
// if (csL.hasVersion() && csR.hasVersion()) {
// if (csL.getVersion().equals(csR.getVersion())) {
// r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null).span(2).center());
// } else {
// r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
// r.getCells().add(gen.new Cell(null, null, csR.getVersion(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
// }
// } else if (csL.hasVersion()) {
// r.getCells().add(gen.new Cell(null, null, csL.getVersion(), null, null));
// r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));
// } else if (csR.hasVersion()) {
// r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));
// r.getCells().add(gen.new Cell(null, null, csR.getVersion(), null, null));
// } else {
// r.getCells().add(missingCell(gen).span(2).center());
// }
//
// } else if (t.hasLeft()) {
// r.setColor(COLOR_NO_ROW_RIGHT);
// ConceptSetComponent cs = (ConceptSetComponent) t.getLeft();
// r.getCells().add(gen.new Cell(null, null, cs.getSystem(), null, null));
// r.getCells().add(missingCell(gen));
// r.getCells().add(gen.new Cell(null, null, cs.hasVersion() ? "Version: "+cs.getVersion() : "", null, null));
// r.getCells().add(missingCell(gen));
// } else {
// r.setColor(COLOR_NO_ROW_LEFT);
// ConceptSetComponent cs = (ConceptSetComponent) t.getRight();
// r.getCells().add(missingCell(gen));
// r.getCells().add(gen.new Cell(null, null, cs.getSystem(), null, null));
// r.getCells().add(missingCell(gen));
// r.getCells().add(gen.new Cell(null, null, cs.hasVersion() ? "Version: "+cs.getVersion() : "", null, null));
// }
// r.getCells().add(gen.new Cell(null, null, t.getError(), null, null));
}
public XhtmlNode renderExpansion(ValueSetComparison csc, String id, String prefix) throws IOException {
if (csc.getExpansion() == null) {
XhtmlNode p = new XhtmlNode(NodeType.Element, "p");
p.tx("Unable to generate expansion - see errors");
return p;
}
if (csc.getExpansion().getChildren().isEmpty()) {
XhtmlNode p = new XhtmlNode(NodeType.Element, "p");
p.tx("Expansion is empty");
return p;
}
// columns: code(+system), version, display , abstract, inactive,
boolean hasSystem = csc.getExpansion().getChildren().isEmpty() ? false : getSystemVaries(csc.getExpansion(), csc.getExpansion().getChildren().get(0).either().getSystem());
boolean hasVersion = findVersion(csc.getExpansion());
boolean hasAbstract = findAbstract(csc.getExpansion());
boolean hasInactive = findInactive(csc.getExpansion());
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(new RenderingI18nContext(), Utilities.path("[tmp]", "comparison"), false, "c");
TableModel model = gen.new TableModel(id, true);
model.setAlternating(true);
if (hasSystem) {
model.getTitles().add(gen.new Title(null, null, "System", "The code for the concept", null, 100));
}
model.getTitles().add(gen.new Title(null, null, "Code", "The system for the concept", null, 100));
model.getTitles().add(gen.new Title(null, null, "Display", "The display for the concept", null, 200, 2));
// if (hasVersion) {
// model.getTitles().add(gen.new Title(null, null, "Version", "The version for the concept", null, 200, 2));
// }
// if (hasAbstract) {
// model.getTitles().add(gen.new Title(null, null, "Abstract", "The abstract flag for the concept", null, 200, 2));
// }
// if (hasInactive) {
// model.getTitles().add(gen.new Title(null, null, "Inactive", "The inactive flag for the concept", null, 200, 2));
// }
model.getTitles().add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200));
for (StructuralMatch t : csc.getExpansion().getChildren()) {
addExpansionRow(gen, model.getRows(), t, hasSystem, hasVersion, hasAbstract, hasInactive);
}
return gen.generate(model, prefix, 0, null);
}
private void addExpansionRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, boolean hasSystem, boolean hasVersion, boolean hasAbstract, boolean hasInactive) {
Row r = gen.new Row();
rows.add(r);
if (hasSystem) {
r.getCells().add(gen.new Cell(null, null, t.either().getSystem(), null, null));
}
r.getCells().add(gen.new Cell(null, null, t.either().getCode(), null, null));
if (t.hasLeft() && t.hasRight()) {
if (t.getLeft().hasDisplay() && t.getRight().hasDisplay()) {
if (t.getLeft().getDisplay().equals(t.getRight().getDisplay())) {
r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null).span(2).center());
} else {
r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
r.getCells().add(gen.new Cell(null, null, t.getRight().getDisplay(), null, null).setStyle("background-color: "+COLOR_DIFFERENT));
}
} else if (t.getLeft().hasDisplay()) {
r.getCells().add(gen.new Cell(null, null, t.getLeft().getDisplay(), null, null));
r.getCells().add(missingCell(gen, COLOR_NO_CELL_RIGHT));
} else if (t.getRight().hasDisplay()) {
r.getCells().add(missingCell(gen, COLOR_NO_CELL_LEFT));
r.getCells().add(gen.new Cell(null, null, t.getRight().getDisplay(), null, null));
} else {
r.getCells().add(missingCell(gen).span(2).center());
}
} else if (t.hasLeft()) {
r.setColor(COLOR_NO_ROW_RIGHT);
r.getCells().add(gen.new Cell(null, null, t.either().getDisplay(), null, null));
r.getCells().add(missingCell(gen));
} else {
r.setColor(COLOR_NO_ROW_LEFT);
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, t.either().getDisplay(), null, null));
}
r.getCells().add(cellForMessages(gen, t.getMessages()));
for (StructuralMatch c : t.getChildren()) {
addExpansionRow(gen, r.getSubRows(), c, hasSystem, hasVersion, hasAbstract, hasInactive);
}
}
private boolean getSystemVaries(StructuralMatch list, String system) {
for (StructuralMatch t : list.getChildren()) {
if (t.hasLeft() && !system.equals(t.getLeft().getSystem())) {
return true;
}
if (t.hasRight() && !system.equals(t.getRight().getSystem())) {
return true;
}
if (getSystemVaries(t, system)) {
return true;
}
}
return false;
}
private boolean findInactive(StructuralMatch list) {
for (StructuralMatch t : list.getChildren()) {
if (t.hasLeft() && t.getLeft().getInactive()) {
return true;
}
if (t.hasRight() && t.getRight().getInactive()) {
return true;
}
if (findInactive(t)) {
return true;
}
}
return false;
}
private boolean findAbstract(StructuralMatch list) {
for (StructuralMatch t : list.getChildren()) {
if (t.hasLeft() && t.getLeft().getAbstract()) {
return true;
}
if (t.hasRight() && t.getRight().getAbstract()) {
return true;
}
if (findAbstract(t)) {
return true;
}
}
return false;
}
private boolean findVersion(StructuralMatch list) {
for (StructuralMatch t : list.getChildren()) {
if (t.hasLeft() && t.getLeft().hasVersion()) {
return true;
}
if (t.hasRight() && t.getRight().hasVersion()) {
return true;
}
if (findVersion(t)) {
return true;
}
}
return false;
}
public XhtmlNode renderUnion(ValueSetComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
ValueSetRenderer vsr = new ValueSetRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER));
return vsr.buildNarrative(ResourceWrapper.forResource(vsr.getContext(), comp.union));
}
public XhtmlNode renderIntersection(ValueSetComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome {
ValueSetRenderer vsr = new ValueSetRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER));
return vsr.buildNarrative(ResourceWrapper.forResource(vsr.getContext(), comp.intersection));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy