Work on comparison and rendering for showing differences properly

This commit is contained in:
Grahame Grieve 2023-08-15 19:33:39 +10:00
parent ebc755bc2a
commit ced8a36f37
11 changed files with 1711 additions and 102 deletions

View File

@ -232,6 +232,10 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
}
return b.toString();
}
public boolean noUpdates() {
return !(changedMetadata.noteable() || changedDefinitions.noteable() || !changedContent.noteable() || !changedContentInterpretation.noteable());
}
}
public CanonicalResourceComparer(ComparisonSession session) {

View File

@ -33,8 +33,8 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
public class CodeSystemComparison extends CanonicalResourceComparison<CodeSystem> {
private StructuralMatch<PropertyComponent> properties;
private StructuralMatch<CodeSystemFilterComponent> filters;
private StructuralMatch<PropertyComponent> properties = new StructuralMatch<PropertyComponent>();
private StructuralMatch<CodeSystemFilterComponent> filters = new StructuralMatch<CodeSystemFilterComponent>();
private StructuralMatch<ConceptDefinitionComponent> combined;
private Map<String, String> propMap = new HashMap<>(); // right to left; left retains it's name
public CodeSystemComparison(CodeSystem left, CodeSystem right) {

View File

@ -115,6 +115,7 @@ public class ComparisonSession {
return csc;
}
} else if (left != null) {
VersionComparisonAnnotation.markDeleted(null, forVersion, left.fhirType(), left); // todo: waht?
String key = key(left.getUrl(), left.getVersion(), left.getUrl(), left.getVersion());
if (compares.containsKey(key)) {
return compares.get(key);
@ -123,6 +124,7 @@ public class ComparisonSession {
compares.put(key, csc);
return csc;
} else {
VersionComparisonAnnotation.markAdded(right, forVersion);
String key = key(right.getUrl(), right.getVersion(), right.getUrl(), right.getVersion());
if (compares.containsKey(key)) {
return compares.get(key);

View File

@ -173,7 +173,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
res.combined = sm;
ln = new DefinitionNavigator(session.getContextLeft(), left, true);
rn = new DefinitionNavigator(session.getContextRight(), right, true);
ch = compareDiff(ln.path(), null, ln, rn) || ch;
ch = compareDiff(ln.path(), null, ln, rn, res) || ch;
// we don't preserve the differences - we only want the annotations
}
res.updateDefinitionsState(ch);
@ -389,7 +389,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
}
private boolean compareDiff(String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
private boolean compareDiff(String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right, ProfileComparison res) throws DefinitionException, FHIRFormatError, IOException {
assert(path != null);
assert(left != null);
assert(right != null);
@ -414,7 +414,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
def = comparePrimitivesWithTracking("max", left.current().getMaxElement(), right.current().getMaxElement(), null, IssueSeverity.INFORMATION, null, right.current(), session.getForVersion()) || def;
// add the children
def = compareDiffChildren(path, left, right) || def;
def = compareDiffChildren(path, left, right, right.current(), res) || def;
//
// // now process the slices
// if (left.current().hasSlicing() || right.current().hasSlicing()) {
@ -478,31 +478,26 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
}
private boolean compareDiffChildren(String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError {
private boolean compareDiffChildren(String path, DefinitionNavigator left, DefinitionNavigator right, Base parent, ProfileComparison res) throws DefinitionException, IOException, FHIRFormatError {
boolean def = false;
List<DefinitionNavigator> lc = left.children();
List<DefinitionNavigator> rc = right.children();
// it's possible that one of these profiles walks into a data type and the other doesn't
// if it does, we have to load the children for that data into the profile that doesn't
// walk into it
if (lc.isEmpty() && !rc.isEmpty() && right.current().getType().size() == 1 && left.hasTypeChildren(right.current().getType().get(0), left.getStructure()))
lc = left.childrenFromType(right.current().getType().get(0), right.getStructure());
if (rc.isEmpty() && !lc.isEmpty() && left.current().getType().size() == 1 && right.hasTypeChildren(left.current().getType().get(0), right.getStructure()))
rc = right.childrenFromType(left.current().getType().get(0), left.getStructure());
List<DefinitionNavigator> matchR = new ArrayList<>();
for (DefinitionNavigator l : lc) {
DefinitionNavigator r = findInList(rc, l);
if (r == null) {
// todo
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "element", l.current());
res.updateContentState(true);
} else {
def = compareDiff(l.path(), null, l, r) || def;
def = compareDiff(l.path(), null, l, r, res) || def;
}
}
for (DefinitionNavigator r : rc) {
if (!matchR.contains(r)) {
// todo
VersionComparisonAnnotation.markAdded(r.current(), session.getForVersion());
res.updateContentState(true);
}
}
return def;

View File

@ -142,7 +142,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
def = compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()) || def;
res.updateDefinitionsState(def);
compareExpansions(left, right, res);
// compareExpansions(left, right, res);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -166,7 +166,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
intersection.getInclude().add(csI);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r);
res.getIncludes().getChildren().add(sm);
def = compareDefinitions(l, r, sm, csM, csI, res) || def;
def = compareDefinitions("ValueSet.compose.exclude["+right.getInclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def;
}
}
for (ConceptSetComponent r : right.getInclude()) {
@ -194,7 +194,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
intersection.getExclude().add(csI);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r);
res.getExcludes().getChildren().add(sm);
def = compareDefinitions(l, r, sm, csM, csI, res) || def;
def = compareDefinitions("ValueSet.compose.exclude["+right.getExclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def;
}
}
for (ConceptSetComponent r : right.getExclude()) {
@ -241,7 +241,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
}
private boolean compareDefinitions(ConceptSetComponent left, ConceptSetComponent right, StructuralMatch<Element> combined, ConceptSetComponent union, ConceptSetComponent intersection, ValueSetComparison res) {
private boolean compareDefinitions(String path, ConceptSetComponent left, ConceptSetComponent right, StructuralMatch<Element> 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<CanonicalType> matchVSR = new ArrayList<>();
@ -250,7 +250,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (r == null) {
union.getValueSet().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed ValueSet", "ValueSet.compose.include.valueSet")));
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.ERROR, "Removed ValueSet", "ValueSet.compose.include.valueSet")));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "valueset", l);
}
@ -266,7 +266,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getValueSet().add(l);
union.getValueSet().add(r);
res.updateContentState(true);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.INFORMATION, "Values are different", "ValueSet.compose.include.valueSet"));
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.WARNING, "Values are different", "ValueSet.compose.include.valueSet"));
combined.getChildren().add(sm);
if (session.isAnnotate()) {
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
@ -279,7 +279,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchVSR.contains(r)) {
union.getValueSet().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.ERROR, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -290,7 +290,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (r == null) {
union.getConcept().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this Concept", "ValueSet.compose.include.concept")));
combined.getChildren().add(new StructuralMatch<Element>(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));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "concept", l);
} else {
matchCR.add(r);
@ -301,15 +302,15 @@ public class ValueSetComparer extends CanonicalResourceComparer {
intersection.getConcept().add(ci);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r);
combined.getChildren().add(sm);
def = compareConcepts(l, r, sm, cu, ci) || def;
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<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.INFORMATION, "Concepts are different", "ValueSet.compose.include.concept"));
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.WARNING, "Concepts are different", "ValueSet.compose.include.concept"));
combined.getChildren().add(sm);
res.updateContentState(true);
compareConcepts(l, r, sm, null, null);
compareConcepts(path+".concept["+right.getConcept().indexOf(r)+"]", l, r, sm, null, null, res);
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
}
@ -318,7 +319,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchCR.contains(r)) {
union.getConcept().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this Concept", "ValueSet.compose.include.concept"), r));
combined.getChildren().add(new StructuralMatch<Element>(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));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -329,7 +331,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (r == null) {
union.getFilter().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", "ValueSet.compose.include.filter")));
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.ERROR, "Removed this item", "ValueSet.compose.include.filter")));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "filter", l);
} else {
matchFR.add(r);
@ -347,7 +349,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
} else {
union.getFilter().add(l);
union.getFilter().add(r);
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r, vmI(IssueSeverity.INFORMATION, "Codes are different", "ValueSet.compose.include.filter"));
StructuralMatch<Element> sm = new StructuralMatch<Element>(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);
@ -358,14 +360,14 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (!matchFR.contains(r)) {
union.getFilter().add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this item", "ValueSet.compose.include.filter"), r));
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.ERROR, "Added this item", "ValueSet.compose.include.filter"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;
}
private boolean compareConcepts(ConceptReferenceComponent l, ConceptReferenceComponent r, StructuralMatch<Element> sm, ConceptReferenceComponent cu, ConceptReferenceComponent ci) {
private boolean compareConcepts(String path, ConceptReferenceComponent l, ConceptReferenceComponent r, StructuralMatch<Element> sm, ConceptReferenceComponent cu, ConceptReferenceComponent ci, ValueSetComparison res) {
boolean def = false;
sm.getChildren().add(new StructuralMatch<Element>(l.getCodeElement(), r.getCodeElement(), l.getCode().equals(r.getCode()) ? null : vmI(IssueSeverity.INFORMATION, "Codes do not match", "ValueSet.compose.include.concept")));
if (ci != null) {
@ -379,7 +381,13 @@ public class ValueSetComparer extends CanonicalResourceComparer {
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));
VersionComparisonAnnotation.markChanged(r.getDisplayElement(), session.getForVersion());
}
} else if (l.hasDisplay()) {
VersionComparisonAnnotation.markDeleted(r, session.getForVersion(), "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<Element>(l.getDisplayElement(), null, vmI(IssueSeverity.INFORMATION, "Display Removed", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setDisplay(l.getDisplay());
@ -387,6 +395,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
}
def = true;
} else if (r.hasDisplay()) {
VersionComparisonAnnotation.markAdded(r.getDisplayElement(), session.getForVersion());
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, "Code "+l.getCode()+" display '"+r.getDisplay()+"' added", IssueSeverity.WARNING));
sm.getChildren().add(new StructuralMatch<Element>(null, r.getDisplayElement(), vmI(IssueSeverity.INFORMATION, "Display added", "ValueSet.compose.include.concept")));
if (ci != null) {
ci.setDisplay(r.getDisplay());

View File

@ -9,13 +9,17 @@ import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceCom
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class VersionComparisonAnnotation {
public enum AnotationType {
NoChange, Added, Changed, Deleted;
NoChange, Added, Changed, ChildrenDeleted, Deleted;
}
public static final String USER_DATA_NAME = "version-annotation";
@ -37,7 +41,7 @@ public class VersionComparisonAnnotation {
public static void annotate(Base base, String version, CanonicalResourceComparison<? extends CanonicalResource> comp) {
if (version != null) {
VersionComparisonAnnotation vca = new VersionComparisonAnnotation(comp.noChange() ? AnotationType.NoChange : AnotationType.Added, version);
VersionComparisonAnnotation vca = new VersionComparisonAnnotation(comp.noUpdates() ? AnotationType.NoChange : AnotationType.Changed, version);
vca.comp = comp;
base.setUserData(USER_DATA_NAME, vca);
}
@ -57,13 +61,13 @@ public class VersionComparisonAnnotation {
}
public static void markDeleted(Base parent, String version, String name, Base other) {
if (version != null) {
if (version != null && other != null) {
VersionComparisonAnnotation vca = null;
if (parent.hasUserData(USER_DATA_NAME)) {
vca = (VersionComparisonAnnotation) parent.getUserData(USER_DATA_NAME);
assert vca.type != AnotationType.Added;
} else {
vca = new VersionComparisonAnnotation(AnotationType.Changed, version);
vca = new VersionComparisonAnnotation(AnotationType.ChildrenDeleted, version);
parent.setUserData(USER_DATA_NAME, vca);
}
if (vca.deletedChildren == null) {
@ -103,13 +107,13 @@ public class VersionComparisonAnnotation {
return spanOuter;
case Changed:
spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been changed since "+version);
spanInner.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed:");
return spanOuter;
case Deleted:
spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner = spanOuter.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since "+version);
spanInner.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return spanOuter.strikethrough();
@ -137,13 +141,13 @@ public class VersionComparisonAnnotation {
return divOuter;
case Changed:
divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been changed since "+version);
spanInner.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed:");
return divOuter;
case Deleted:
divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner = divOuter.para().style("margin: 0").span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since "+version);
spanInner.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return divOuter.strikethrough();
@ -151,6 +155,51 @@ public class VersionComparisonAnnotation {
return x;
}
}
public static XhtmlNode renderRow(Base b, XhtmlNode tbl, XhtmlNode tr) {
if (b.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME);
return self.renderRow(tbl, tr);
} else {
return tr.td();
}
}
private XhtmlNode renderRow(XhtmlNode tbl, XhtmlNode tr) {
switch (type) {
case Added:
if (tbl.isClass("grid")) {
tr.style("border: solid 1px #dddddd; margin: 2px; padding: 2px");
}
XhtmlNode td = tr.td();
XhtmlNode span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This row of content has been added since "+version);
span.img("icon-change-add.png", "icon");
span.tx(" Added:");
XhtmlNode x = new XhtmlNode(NodeType.Element, "holder");
x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This row of content has been added since "+version).tx(" ");
tr.styleCells(x);
return td;
case Changed:
td = tr.td();
span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This row of content has been changed since "+version);
span.img("icon-change-edit.png", "icon");
span.tx(" Changed:");
return td;
case Deleted:
tr.style("text-decoration: line-through");
td = tr.td();
span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since "+version);
span.img("icon-change-remove.png", "icon");
span.tx(" Removed:");
x = new XhtmlNode(NodeType.Element, "holder");
x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px; text-decoration: none", "This row of content has been added since "+version).tx(" ");
tr.styleCells(x);
return td;
default:
return tr.td();
}
}
public static boolean hasDeleted(Base base, String... names) {
boolean result = false;
@ -178,6 +227,18 @@ public class VersionComparisonAnnotation {
return result;
}
public static Base getDeletedItem(Base base, String name) {
List<Base> result = new ArrayList<>();
if (base.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME);
if (self.deletedChildren != null && self.deletedChildren.containsKey(name)) {
result.addAll(self.deletedChildren.get(name));
}
}
return result.isEmpty() ? null : result.get(0);
}
public static CanonicalResourceComparison<? extends CanonicalResource> artifactComparison(Base base) {
if (base.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME);
@ -186,5 +247,34 @@ public class VersionComparisonAnnotation {
return null;
}
}
public static void renderSummary(Base base, XhtmlNode x, String version) {
if (base.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME);
switch (self.type) {
case Added:
XhtmlNode spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-add.png", "icon");
spanInner.tx(" Added");
return;
case Changed:
spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed");
return;
case Deleted:
spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
spanInner.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed");
return;
default:
x.span("color: #eeeeee").tx("n/c");
return;
}
} else {
x.span("color: #eeeeee").tx("--");
}
}
}

View File

@ -398,7 +398,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
tr.setAttribute("style", "background-color: #ffeeee");
}
XhtmlNode td = tr.td();
XhtmlNode td = VersionComparisonAnnotation.renderRow(c, t, tr);
if (hasHierarchy) {
td.addText(Integer.toString(level+1));
td = tr.td();
@ -407,9 +407,9 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
String link = isSupplement ? getLinkForCode(cs.getSupplements(), null, c.getCode()) : null;
if (link != null) {
td.ah(link).attribute("style", "white-space:nowrap").addText(c.getCode());
td.ah(link).style( "white-space:nowrap").addText(c.getCode());
} else {
VersionComparisonAnnotation.render(c, td.attribute("style", "white-space:nowrap")).addText(c.getCode());
td.style("white-space:nowrap").addText(c.getCode());
}
XhtmlNode a;
if (c.hasCodeElement()) {
@ -579,7 +579,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
td = tr.td();
String s = Utilities.padLeft("", '\u00A0', (level+1)*2);
td.addText(s);
td.attribute("style", "white-space:nowrap");
td.style("white-space:nowrap");
a = td.ah("#"+cs.getId()+"-" + Utilities.nmtokenize(cc.getCode()));
a.addText(cc.getCode());
if (hasDisplay) {

View File

@ -30,6 +30,7 @@ import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.ExtensionHelper;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent;
@ -57,6 +58,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.ibm.icu.impl.locale.StringTokenIterator;
public class ValueSetRenderer extends TerminologyRenderer {
@ -1175,50 +1177,10 @@ public class ValueSetRenderer extends TerminologyRenderer {
hasExtensions = true;
addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null, langs, designations, doDesignations), maps);
for (ConceptReferenceComponent c : inc.getConcept()) {
XhtmlNode tr = t.tr();
XhtmlNode td = tr.td();
ConceptDefinitionComponent cc = definitions == null ? null : definitions.get(c.getCode());
addCodeToTable(false, inc.getSystem(), c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
td = tr.td();
if (!Utilities.noString(c.getDisplay()))
td.addText(c.getDisplay());
else if (cc != null && !Utilities.noString(cc.getDisplay()))
td.addText(cc.getDisplay());
if (hasDefinition) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) {
smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION));
} else if (cc != null && !Utilities.noString(cc.getDefinition())) {
smartAddText(td, cc.getDefinition());
}
}
if (hasComments) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) {
smartAddText(td, "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT));
}
}
if (doDesignations) {
addDesignationsToRow(c, designations, tr);
addLangaugesToRow(c, langs, tr);
}
for (UsedConceptMap m : maps) {
td = tr.td();
List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap());
boolean first = true;
for (TargetElementComponentWrapper mapping : mappings) {
if (!first)
td.br();
first = false;
XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString());
span.addText(getCharForRelationship(mapping.comp));
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("("+mapping.comp.getComment()+")");
}
}
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, c);
}
for (Base b : VersionComparisonAnnotation.getDeleted(inc, "concept" )) {
renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, (ConceptReferenceComponent) b);
}
}
if (inc.getFilter().size() > 0) {
@ -1306,6 +1268,58 @@ public class ValueSetRenderer extends TerminologyRenderer {
return hasExtensions;
}
private void renderConcept(ConceptSetComponent inc, List<String> langs, boolean doDesignations,
List<UsedConceptMap> maps, Map<String, String> designations, Map<String, ConceptDefinitionComponent> definitions,
XhtmlNode t, boolean hasComments, boolean hasDefinition, ConceptReferenceComponent c) {
XhtmlNode tr = t.tr();
XhtmlNode td = VersionComparisonAnnotation.renderRow(c, t, tr);
ConceptDefinitionComponent cc = definitions == null ? null : definitions.get(c.getCode());
addCodeToTable(false, inc.getSystem(), c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
td = tr.td();
if (!Utilities.noString(c.getDisplay()))
VersionComparisonAnnotation.render(c.getDisplayElement(), td).addText(c.getDisplay());
else if (VersionComparisonAnnotation.hasDeleted(c, "display")) {
StringType d = (StringType) VersionComparisonAnnotation.getDeletedItem(c, "display");
VersionComparisonAnnotation.render(d, td).addText(d.primitiveValue());
} else if (cc != null && !Utilities.noString(cc.getDisplay()))
td.style("color: #cccccc").addText(cc.getDisplay());
if (hasDefinition) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) {
smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION));
} else if (cc != null && !Utilities.noString(cc.getDefinition())) {
smartAddText(td, cc.getDefinition());
}
}
if (hasComments) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) {
smartAddText(td, "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT));
}
}
if (doDesignations) {
addDesignationsToRow(c, designations, tr);
addLangaugesToRow(c, langs, tr);
}
for (UsedConceptMap m : maps) {
td = tr.td();
List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap());
boolean first = true;
for (TargetElementComponentWrapper mapping : mappings) {
if (!first)
td.br();
first = false;
XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString());
span.addText(getCharForRelationship(mapping.comp));
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("("+mapping.comp.getComment()+")");
}
}
}
public void addDesignationsToRow(ConceptReferenceComponent c, Map<String, String> designations, XhtmlNode tr) {
for (String url : designations.keySet()) {
String d = null;

View File

@ -79,6 +79,7 @@ public class RenderingContext {
SUMMARY, // 5 cells: tree/name | flags | cardinality | type | details
BINDINGS, // tree/name + column for each kind of binding found, cells are lists of bindings
OBLIGATIONS, // tree/name + column for each actor that has obligations
DATA_DICT, // detailed element view
}
public enum ExampleScenarioRendererMode {
@ -132,6 +133,7 @@ public class RenderingContext {
LINKS
}
public enum KnownLinkType {
SELF, // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools)
SPEC, // version specific link to core specification

View File

@ -138,8 +138,8 @@ public class DefinitionNavigator {
if (nameMap.containsKey(path)) {
DefinitionNavigator master = nameMap.get(path);
ElementDefinition cm = master.current();
if (!cm.hasSlicing())
throw new DefinitionException("Found slices with no slicing details at "+dn.current().getPath());
// if (!cm.hasSlicing())
// throw new DefinitionException("Found slices with no slicing details at "+dn.current().getPath());
if (master.slices == null)
master.slices = new ArrayList<DefinitionNavigator>();
master.slices.add(dn);