
org.hl7.fhir.r4b.comparison.CanonicalResourceComparer Maven / Gradle / Ivy
The newest version!
package org.hl7.fhir.r4b.comparison;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4b.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r4b.model.CanonicalResource;
import org.hl7.fhir.r4b.model.CanonicalType;
import org.hl7.fhir.r4b.model.CapabilityStatement;
import org.hl7.fhir.r4b.model.CodeType;
import org.hl7.fhir.r4b.model.CodeableConcept;
import org.hl7.fhir.r4b.model.Coding;
import org.hl7.fhir.r4b.model.PrimitiveType;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
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.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.XhtmlNode;
public abstract class CanonicalResourceComparer extends ResourceComparer {
public abstract class CanonicalResourceComparison extends ResourceComparison {
protected T left;
protected T right;
protected T union;
protected T intersection;
protected Map> metadata = new HashMap<>();
public CanonicalResourceComparison(T left, T right) {
super(left.getId(), right.getId());
this.left = left;
this.right = right;
}
public T getLeft() {
return left;
}
public T getRight() {
return right;
}
public T getUnion() {
return union;
}
public T getIntersection() {
return intersection;
}
public Map> getMetadata() {
return metadata;
}
public void setLeft(T left) {
this.left = left;
}
public void setRight(T right) {
this.right = right;
}
public void setUnion(T union) {
this.union = union;
}
public void setIntersection(T intersection) {
this.intersection = intersection;
}
@Override
protected String toTable() {
String s = "";
s = s + refCell(left);
s = s + refCell(right);
s = s + "Comparison ";
s = s + "" + outcomeSummary() + " ";
return "" + s + " \r\n";
}
@Override
protected void countMessages(MessageCounts cnts) {
for (StructuralMatch sm : metadata.values()) {
sm.countMessages(cnts);
}
}
}
public CanonicalResourceComparer(ComparisonSession session) {
super(session);
}
protected void compareMetadata(CanonicalResource left, CanonicalResource right,
Map> comp, CanonicalResourceComparison extends CanonicalResource> res) {
comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res);
comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res);
comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res);
comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res);
comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION,
res);
comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp,
IssueSeverity.WARNING, res);
comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res);
comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp,
IssueSeverity.INFORMATION, res);
comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp,
IssueSeverity.NULL, res);
comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res);
comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp,
IssueSeverity.INFORMATION, res);
compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp,
IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction());
}
protected void compareCodeableConceptList(String name, List left, List right,
Map> comp, IssueSeverity level,
CanonicalResourceComparison extends CanonicalResource> res, List union,
List intersection) {
List matchR = new ArrayList<>();
StructuralMatch combined = new StructuralMatch();
for (CodeableConcept l : left) {
CodeableConcept r = findCodeableConceptInList(right, l);
if (r == null) {
union.add(l);
combined.getChildren().add(new StructuralMatch(gen(l), vm(IssueSeverity.INFORMATION,
"Removed the item '" + gen(l) + "'", fhirType() + "." + name, res.getMessages())));
} else {
matchR.add(r);
union.add(r);
intersection.add(r);
StructuralMatch sm = new StructuralMatch(gen(l), gen(r));
combined.getChildren().add(sm);
}
}
for (CodeableConcept r : right) {
if (!matchR.contains(r)) {
union.add(r);
combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
"Added the item '" + gen(r) + "'", fhirType() + "." + name, res.getMessages()), gen(r)));
}
}
comp.put(name, combined);
}
private CodeableConcept findCodeableConceptInList(List list, CodeableConcept item) {
for (CodeableConcept t : list) {
if (t.matches(item)) {
return t;
}
}
return null;
}
protected String gen(CodeableConcept cc) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (Coding c : cc.getCoding()) {
b.append(gen(c));
}
return b.toString();
}
protected String gen(Coding c) {
return c.getSystem() + (c.hasVersion() ? "|" + c.getVersion() : "") + "#" + c.getCode();
}
protected void compareCanonicalList(String name, List left, List right,
Map> comp, IssueSeverity level,
CanonicalResourceComparison extends CanonicalResource> res, List union,
List intersection) {
List matchR = new ArrayList<>();
StructuralMatch combined = new StructuralMatch();
for (CanonicalType l : left) {
CanonicalType r = findCanonicalInList(right, l);
if (r == null) {
union.add(l);
combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION,
"Removed the item '" + l.getValue() + "'", fhirType() + "." + name, res.getMessages())));
} else {
matchR.add(r);
union.add(r);
intersection.add(r);
StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue());
combined.getChildren().add(sm);
}
}
for (CanonicalType r : right) {
if (!matchR.contains(r)) {
union.add(r);
combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
"Added the item '" + r.getValue() + "'", fhirType() + "." + name, res.getMessages()), r.getValue()));
}
}
comp.put(name, combined);
}
private CanonicalType findCanonicalInList(List list, CanonicalType item) {
for (CanonicalType t : list) {
if (t.getValue().equals(item.getValue())) {
return t;
}
}
return null;
}
protected void compareCodeList(String name, List left, List right,
Map> comp, IssueSeverity level,
CanonicalResourceComparison extends CanonicalResource> res, List union, List intersection) {
List matchR = new ArrayList<>();
StructuralMatch combined = new StructuralMatch();
for (CodeType l : left) {
CodeType r = findCodeInList(right, l);
if (r == null) {
union.add(l);
combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION,
"Removed the item '" + l.getValue() + "'", fhirType() + "." + name, res.getMessages())));
} else {
matchR.add(r);
union.add(r);
intersection.add(r);
StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue());
combined.getChildren().add(sm);
}
}
for (CodeType r : right) {
if (!matchR.contains(r)) {
union.add(r);
combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION,
"Added the item '" + r.getValue() + "'", fhirType() + "." + name, res.getMessages()), r.getValue()));
}
}
comp.put(name, combined);
}
private CodeType findCodeInList(List list, CodeType item) {
for (CodeType t : list) {
if (t.getValue().equals(item.getValue())) {
return t;
}
}
return null;
}
@SuppressWarnings("rawtypes")
protected void comparePrimitives(String name, PrimitiveType l, PrimitiveType r,
Map> comp, IssueSeverity level,
CanonicalResourceComparison extends CanonicalResource> res) {
StructuralMatch match = null;
if (l.isEmpty() && r.isEmpty()) {
match = new StructuralMatch<>(null, null, null);
} else if (l.isEmpty()) {
match = new StructuralMatch<>(null, r.primitiveValue(),
vmI(IssueSeverity.INFORMATION, "Added the item '" + r.primitiveValue() + "'", fhirType() + "." + name));
} else if (r.isEmpty()) {
match = new StructuralMatch<>(l.primitiveValue(), null,
vmI(IssueSeverity.INFORMATION, "Removed the item '" + l.primitiveValue() + "'", fhirType() + "." + name));
} else if (!l.hasValue() && !r.hasValue()) {
match = new StructuralMatch<>(null, null, vmI(IssueSeverity.INFORMATION, "No Value", fhirType() + "." + name));
} else if (!l.hasValue()) {
match = new StructuralMatch<>(null, r.primitiveValue(),
vmI(IssueSeverity.INFORMATION, "No Value on Left", fhirType() + "." + name));
} else if (!r.hasValue()) {
match = new StructuralMatch<>(l.primitiveValue(), null,
vmI(IssueSeverity.INFORMATION, "No Value on Right", fhirType() + "." + name));
} else if (l.getValue().equals(r.getValue())) {
match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(), null);
} else {
match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(),
vmI(level, "Values Differ", fhirType() + "." + name));
if (level != IssueSeverity.NULL) {
res.getMessages()
.add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, fhirType() + "." + name,
"Values for " + name + " differ: '" + l.primitiveValue() + "' vs '" + r.primitiveValue() + "'", level));
}
}
comp.put(name, match);
}
protected abstract String fhirType();
public XhtmlNode renderMetadata(CanonicalResourceComparison extends CanonicalResource> comparison, String id,
String prefix) throws FHIRException, IOException {
// columns: code, display (left|right), properties (left|right)
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(new RenderingI18nContext(), Utilities.path("[tmp]", "compare"), false);
TableModel model = gen.new TableModel(id, true);
model.setAlternating(true);
model.getTitles().add(gen.new Title(null, null, "Name", "Property Name", null, 100));
model.getTitles().add(gen.new Title(null, null, "Value", "The value of the property", null, 200, 2));
model.getTitles()
.add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200));
for (String n : sorted(comparison.getMetadata().keySet())) {
StructuralMatch t = comparison.getMetadata().get(n);
addRow(gen, model.getRows(), n, t);
}
return gen.generate(model, prefix, 0, null);
}
private void addRow(HierarchicalTableGenerator gen, List rows, String name, StructuralMatch t) {
Row r = gen.new Row();
rows.add(r);
r.getCells().add(gen.new Cell(null, null, name, null, null));
if (t.hasLeft() && t.hasRight()) {
if (t.getLeft().equals(t.getRight())) {
r.getCells().add(gen.new Cell(null, null, t.getLeft(), null, null).span(2));
} else {
r.getCells()
.add(gen.new Cell(null, null, t.getLeft(), null, null).setStyle("background-color: " + COLOR_DIFFERENT));
r.getCells()
.add(gen.new Cell(null, null, t.getRight(), null, null).setStyle("background-color: " + COLOR_DIFFERENT));
}
} else if (t.hasLeft()) {
r.setColor(COLOR_NO_ROW_RIGHT);
r.getCells().add(gen.new Cell(null, null, t.getLeft(), null, null));
r.getCells().add(missingCell(gen));
} else if (t.hasRight()) {
r.setColor(COLOR_NO_ROW_LEFT);
r.getCells().add(missingCell(gen));
r.getCells().add(gen.new Cell(null, null, t.getRight(), null, null));
} else {
r.getCells().add(missingCell(gen).span(2));
}
r.getCells().add(cellForMessages(gen, t.getMessages()));
int i = 0;
for (StructuralMatch c : t.getChildren()) {
addRow(gen, r.getSubRows(), name + "[" + i + "]", c);
i++;
}
}
private List sorted(Set keys) {
List res = new ArrayList<>();
res.addAll(keys);
Collections.sort(res);
return res;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy