Merge pull request #1392 from hapifhir/2023-08-gg-rendering-overhaul2
2023 08 gg rendering overhaul2
This commit is contained in:
commit
0087a77ec7
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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("--");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -966,6 +966,11 @@ public class I18nConstants {
|
|||
public static final String MSG_DEPENDS_ON_EXTENSION = "MSG_DEPENDS_ON_EXTENSION";
|
||||
public static final String MSG_DEPENDS_ON_PROFILE = "MSG_DEPENDS_ON_PROFILE";
|
||||
public static final String VALIDATION_VAL_STATUS_INCONSISTENT = "VALIDATION_VAL_STATUS_INCONSISTENT";
|
||||
public static final String CODESYSTEM_CS_COUNT_COMPLETE_WRONG = "CODESYSTEM_CS_COUNT_COMPLETE_WRONG";
|
||||
public static final String CODESYSTEM_CS_COUNT_FRAGMENT_WRONG = "CODESYSTEM_CS_COUNT_FRAGMENT_WRONG";
|
||||
public static final String CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = "CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO";
|
||||
public static final String CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = "CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG";
|
||||
public static final String CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = "CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ public abstract class XhtmlFluent {
|
|||
}
|
||||
|
||||
public XhtmlNode td() {
|
||||
return addTag("td");
|
||||
XhtmlNode x = addTag("td");
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode td(String clss) {
|
||||
|
|
|
@ -363,6 +363,10 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
|||
return hasAttributes() && getAttributes().containsKey(name);
|
||||
}
|
||||
|
||||
public boolean hasAttribute(String name, String value) {
|
||||
return hasAttributes() && getAttributes().containsKey(name) && value.equals(getAttributes().get(name));
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return hasAttributes() ? getAttributes().get(name) : null;
|
||||
}
|
||||
|
@ -569,18 +573,25 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT SUPPORTED - Throws {@link UnsupportedOperationException}
|
||||
*/
|
||||
private Map<String, Object> userData;
|
||||
|
||||
public Object getUserData(String theName) {
|
||||
throw new UnsupportedOperationException();
|
||||
if (hasUserData(theName)) {
|
||||
return userData.get(theName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasUserData(String theName) {
|
||||
return userData != null && userData.containsKey(theName);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT SUPPORTED - Throws {@link UnsupportedOperationException}
|
||||
*/
|
||||
public void setUserData(String theName, Object theValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
if (userData == null) {
|
||||
userData = new HashMap<>();
|
||||
}
|
||||
userData.put(theName, theValue);
|
||||
}
|
||||
|
||||
|
||||
|
@ -892,4 +903,23 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isClass(String name) {
|
||||
return hasAttribute("class", name);
|
||||
}
|
||||
|
||||
|
||||
public void styleCells(XhtmlNode x) {
|
||||
setUserData("cells", x);
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode td() {
|
||||
XhtmlNode x = addTag("td");
|
||||
XhtmlNode t = (XhtmlNode) getUserData("cells");
|
||||
if (t != null) {
|
||||
x.copyAllContent(t);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
|
@ -1024,5 +1024,11 @@ MSG_DEPENDS_ON_EXPERIMENTAL = The {0} {1} is an experimental resource
|
|||
MSG_DEPENDS_ON_DRAFT = The {0} {1} is a draft resource
|
||||
MSG_DEPENDS_ON_EXTENSION = extension
|
||||
MSG_DEPENDS_ON_PROFILE = profile
|
||||
VALIDATION_VAL_STATUS_INCONSISTENT = The resource status ''{0}'' amd the standards status ''{1}'' are not consistent
|
||||
VALIDATION_VAL_STATUS_INCONSISTENT = The resource status ''{0}'' and the standards status ''{1}'' are not consistent
|
||||
CODESYSTEM_CS_COUNT_COMPLETE_WRONG = The code system is complete, but the number of concepts ({0}) does not match the stated total number ({1})
|
||||
CODESYSTEM_CS_COUNT_FRAGMENT_WRONG = The code system is a fragment/example, but the number of concepts ({0}) exceeds or matches the stated total number ({1})
|
||||
CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = The code system has no content, but the exceeds the stated total number is 0 concepts - check that this isn't a complete code system that has no concepts, or update/remove the stated count
|
||||
CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = The code system supplement states the total number of concepts as {1}, but this is different to the underlying code system that states a value of {0}
|
||||
CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = The code system says it has no content present, but concepts are found
|
||||
|
||||
|
|
@ -194,7 +194,12 @@
|
|||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.atlassian.commonmark</groupId>
|
||||
<artifactId>commonmark-ext-gfm-tables</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
|||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
@ -32,8 +33,9 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
String caseSensitive = cs.getNamedChildValue("caseSensitive");
|
||||
String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning");
|
||||
String supp = cs.getNamedChildValue("supplements");
|
||||
|
||||
metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp));
|
||||
int count = countConcepts(cs);
|
||||
|
||||
metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp), count, supp);
|
||||
|
||||
String vsu = cs.getNamedChildValue("valueSet");
|
||||
if (!Utilities.noString(vsu)) {
|
||||
|
@ -51,7 +53,6 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet()
|
||||
&& !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu) && ok;
|
||||
if (vs.hasExpansion()) {
|
||||
int count = countConcepts(cs);
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getExpansion().getContains().size() == count, I18nConstants.CODESYSTEM_CS_VS_EXP_MISMATCH, url, vsu, count, vs.getExpansion().getContains().size()) && ok;
|
||||
}
|
||||
} else {
|
||||
|
@ -71,7 +72,11 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
List<Element> concepts = cs.getChildrenByName("concept");
|
||||
int ce = 0;
|
||||
for (Element concept : concepts) {
|
||||
ok = validateSupplementConcept(errors, concept, stack.push(concept, ce, null, null), supp, options) && ok;
|
||||
NodeStack nstack = stack.push(concept, ce, null, null);
|
||||
if (ce == 0) {
|
||||
rule(errors, "2023-08-15", IssueType.INVALID, nstack, !"not-present".equals(content), I18nConstants.CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED);
|
||||
}
|
||||
ok = validateSupplementConcept(errors, concept, nstack, supp, options) && ok;
|
||||
ce++;
|
||||
}
|
||||
} else {
|
||||
|
@ -121,7 +126,7 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement) {
|
||||
private void metaChecks(List<ValidationMessage> errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement, int count, String supp) {
|
||||
if (isSupplement) {
|
||||
if (!"supplement".equals(content)) {
|
||||
NodeStack s = stack.push(cs.getNamedChild("content"), -1, null, null);
|
||||
|
@ -178,6 +183,40 @@ public class CodeSystemValidator extends BaseValidator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cs.hasChild("count")) {
|
||||
int statedCount = Utilities.parseInt(cs.getNamedChildValue("count"), -1);
|
||||
if (statedCount > -1 && content != null) { // error elsewhere
|
||||
var nstack = stack.push(cs.getNamedChild("count"), -1, null, null);
|
||||
switch (content) {
|
||||
case "complete":
|
||||
rule(errors, "2023-08-15", IssueType.INVALID, nstack, count == statedCount, I18nConstants.CODESYSTEM_CS_COUNT_COMPLETE_WRONG, count, statedCount);
|
||||
break;
|
||||
case "example":
|
||||
case "fragment":
|
||||
warning(errors, "2023-08-15", IssueType.INVALID, nstack, count < statedCount, I18nConstants.CODESYSTEM_CS_COUNT_FRAGMENT_WRONG, count, statedCount);
|
||||
break;
|
||||
case "not-present":
|
||||
hint(errors, "2023-08-15", IssueType.INVALID, stack.push(cs.getNamedChild("concept"), -1, null, null), statedCount > 0, I18nConstants.CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO, statedCount);
|
||||
break;
|
||||
case "supplement":
|
||||
CodeSystem css = context.fetchCodeSystem(supp);
|
||||
if (css != null) {
|
||||
rule(errors, "2023-08-15", IssueType.INVALID, nstack, count == css.getCount(), I18nConstants.CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG, css.getCount(), statedCount);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("not-present".equals(content)) {
|
||||
List<Element> concepts = cs.getChildrenByName("concept");
|
||||
if (concepts.size() > 0) {
|
||||
rule(errors, "2023-08-15", IssueType.INVALID, stack.push(concepts.get(0), 0, null, null), false, I18nConstants.CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -45,10 +45,22 @@ import org.hl7.fhir.r5.model.Constants;
|
|||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.renderers.CodeSystemRenderer;
|
||||
import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer;
|
||||
import org.hl7.fhir.r5.renderers.ValueSetRenderer;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode;
|
||||
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.utilities.MarkDownProcessor;
|
||||
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.json.model.*;
|
||||
import org.hl7.fhir.utilities.json.parser.*;
|
||||
import org.hl7.fhir.utilities.npm.CommonPackages;
|
||||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||
|
@ -56,14 +68,13 @@ import org.hl7.fhir.utilities.settings.FhirSettings;
|
|||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class ComparisonTests {
|
||||
|
||||
|
@ -73,9 +84,9 @@ public class ComparisonTests {
|
|||
String contents = TestingUtilities.loadTestResource("comparison", "manifest.json");
|
||||
|
||||
Map<String, JsonObject> examples = new HashMap<String, JsonObject>();
|
||||
manifest = (JsonObject) new com.google.gson.JsonParser().parse(contents);
|
||||
for (Entry<String, JsonElement> e : manifest.getAsJsonObject("test-cases").entrySet()) {
|
||||
examples.put(e.getKey(), e.getValue().getAsJsonObject());
|
||||
manifest = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(contents);
|
||||
for (JsonProperty e : manifest.getJsonObject("test-cases").getProperties()) {
|
||||
examples.put(e.getName(), e.getValue().asJsonObject());
|
||||
}
|
||||
|
||||
List<String> names = new ArrayList<String>(examples.size());
|
||||
|
@ -97,6 +108,8 @@ public class ComparisonTests {
|
|||
private static final String HEADER = "<html><link href=\"http://hl7.org/fhir/fhir.css\" rel=\"stylesheet\"/><body>";
|
||||
private static final String BREAK = "<hr/>";
|
||||
private static final String FOOTER = "</body></html>";
|
||||
private String prefix;
|
||||
private String suffix;
|
||||
|
||||
@ParameterizedTest(name = "{index}: id {0}")
|
||||
@MethodSource("data")
|
||||
|
@ -104,7 +117,7 @@ public class ComparisonTests {
|
|||
TestingUtilities.injectCorePackageLoader();
|
||||
this.content = content;
|
||||
|
||||
if (content.has("use-test") && !content.get("use-test").getAsBoolean())
|
||||
if (content.has("use-test") && !content.asBoolean("use-test"))
|
||||
return;
|
||||
|
||||
if (context == null) {
|
||||
|
@ -132,9 +145,17 @@ public class ComparisonTests {
|
|||
System.out.println("---- " + name + " ----------------------------------------------------------------");
|
||||
CanonicalResource left = load("left");
|
||||
CanonicalResource right = load("right");
|
||||
prefix = loadResource("html-prefix.html");
|
||||
suffix = loadResource("html-suffix.html");
|
||||
|
||||
ComparisonSession session = new ComparisonSession(context, context, "Comparison Tests", null, null);
|
||||
|
||||
if (content.has("version")) {
|
||||
session.setForVersion(content.getJsonObject("version").asString("stated"));
|
||||
session.setAnnotate(true);
|
||||
}
|
||||
RenderingContext lrc = new RenderingContext(context, new MarkDownProcessor(Dialect.COMMON_MARK), null, "http://hl7.org/fhir", "", "en", ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER);
|
||||
lrc.setDestDir(Utilities.path("[tmp]", "comparison"));
|
||||
|
||||
if (left instanceof CodeSystem && right instanceof CodeSystem) {
|
||||
CodeSystemComparer cs = new CodeSystemComparer(session);
|
||||
CodeSystemComparison csc = cs.compare((CodeSystem) left, (CodeSystem) right);
|
||||
|
@ -147,6 +168,8 @@ public class ComparisonTests {
|
|||
String xml2 = new XhtmlComposer(true).compose(cs.renderConcepts(csc, "", ""));
|
||||
TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Concepts") + xml2 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html"));
|
||||
checkOutcomes(csc.getMessages(), content);
|
||||
new CodeSystemRenderer(lrc).render(right);
|
||||
checkOutput(content.getJsonObject("version").asString("filename"), right);
|
||||
} else if (left instanceof ValueSet && right instanceof ValueSet) {
|
||||
ValueSetComparer cs = new ValueSetComparer(session);
|
||||
ValueSetComparison csc = cs.compare((ValueSet) left, (ValueSet) right);
|
||||
|
@ -159,6 +182,8 @@ public class ComparisonTests {
|
|||
String xml3 = new XhtmlComposer(true).compose(cs.renderExpansion(csc, "", ""));
|
||||
TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Definition") + xml2 + BREAK + hd("Expansion") + xml3 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html"));
|
||||
checkOutcomes(csc.getMessages(), content);
|
||||
new ValueSetRenderer(lrc).render(right);
|
||||
checkOutput(content.getJsonObject("version").asString("filename"), right);
|
||||
} else if (left instanceof StructureDefinition && right instanceof StructureDefinition) {
|
||||
ProfileUtilities utils = new ProfileUtilities(context, null, null);
|
||||
genSnapshot(utils, (StructureDefinition) left);
|
||||
|
@ -174,6 +199,14 @@ public class ComparisonTests {
|
|||
// String xml3 = new XhtmlComposer(true).compose(cs.renderExpansion(csc, "", ""));
|
||||
TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Structure") + xml2 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html"));
|
||||
checkOutcomes(csc.getMessages(), content);
|
||||
|
||||
lrc.setStructureMode(StructureDefinitionRendererMode.DATA_DICT);
|
||||
new StructureDefinitionRenderer(lrc).render(right);
|
||||
checkOutput(content.getJsonObject("version").asString("filename-dd"), right);
|
||||
|
||||
lrc.setStructureMode(StructureDefinitionRendererMode.SUMMARY);
|
||||
new StructureDefinitionRenderer(lrc).render(right);
|
||||
checkOutput(content.getJsonObject("version").asString("filename-tree"), right);
|
||||
} else if (left instanceof CapabilityStatement && right instanceof CapabilityStatement) {
|
||||
CapabilityStatementComparer pc = new CapabilityStatementComparer(session);
|
||||
CapabilityStatementComparison csc = pc.compare((CapabilityStatement) left, (CapabilityStatement) right);
|
||||
|
@ -191,6 +224,18 @@ public class ComparisonTests {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkOutput(String name, CanonicalResource right) throws Exception {
|
||||
String output = prefix+ new XhtmlComposer(false, true).compose(right.getText().getDiv()) + suffix;
|
||||
String an = Utilities.path("[tmp]", "comparison", name);
|
||||
TextFile.stringToFile(output, an);
|
||||
String expected = loadResource(name);
|
||||
String en = Utilities.path("[tmp]", "comparison", Utilities.changeFileExt(name, ".expected.html"));
|
||||
TextFile.stringToFile(expected, en);
|
||||
|
||||
String msg = CompareUtilities.checkXMLIsSame(en, an);
|
||||
Assertions.assertTrue(msg == null, "Output does not match expected: "+msg);
|
||||
|
||||
}
|
||||
private void genSnapshot(ProfileUtilities utils, StructureDefinition sd) {
|
||||
StructureDefinition base = context.fetchTypeDefinition(sd.getType());
|
||||
utils.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir/r4", sd.present());
|
||||
|
@ -201,13 +246,18 @@ public class ComparisonTests {
|
|||
}
|
||||
|
||||
private CanonicalResource load(String name) throws IOException {
|
||||
JsonObject details = content.getAsJsonObject(name);
|
||||
String src = TestingUtilities.loadTestResource("comparison", details.get("source").getAsString());
|
||||
return (CanonicalResource) loadResource(details.get("source").getAsString(), src, details.get("version").getAsString());
|
||||
JsonObject details = content.getJsonObject(name);
|
||||
String src = TestingUtilities.loadTestResource("comparison", details.asString("source"));
|
||||
return (CanonicalResource) loadResource(details.asString("source"), src, details.asString("version"));
|
||||
}
|
||||
|
||||
private String loadResource(String name) throws IOException {
|
||||
String src = TestingUtilities.loadTestResource("comparison", name);
|
||||
return src;
|
||||
}
|
||||
|
||||
public Resource loadResource(String filename, String contents, String ver) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
|
||||
try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) {
|
||||
try (InputStream inputStream = IOUtils.toInputStream(contents, StandardCharsets.UTF_8)) {
|
||||
if (filename.contains(".json")) {
|
||||
if (Constants.VERSION.equals(ver) || "5.0".equals(ver))
|
||||
return new JsonParser().parse(inputStream);
|
||||
|
@ -239,7 +289,7 @@ public class ComparisonTests {
|
|||
}
|
||||
|
||||
private void checkOutcomes(List<ValidationMessage> errors, JsonObject focus) {
|
||||
JsonObject output = focus.getAsJsonObject("output");
|
||||
JsonObject output = focus.getJsonObject("output");
|
||||
int ec = 0;
|
||||
int wc = 0;
|
||||
int hc = 0;
|
||||
|
@ -265,11 +315,11 @@ public class ComparisonTests {
|
|||
}
|
||||
}
|
||||
}
|
||||
Assertions.assertEquals(output.get("errorCount").getAsInt(), ec, "Expected " + Integer.toString(output.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + ".");
|
||||
Assertions.assertEquals(output.asInteger("errorCount"), ec, "Expected " + Integer.toString(output.asInteger("errorCount")) + " errors, but found " + Integer.toString(ec) + ".");
|
||||
if (output.has("warningCount"))
|
||||
Assertions.assertEquals(output.get("warningCount").getAsInt(), wc, "Expected " + Integer.toString(output.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + ".");
|
||||
Assertions.assertEquals(output.asInteger("warningCount"), wc, "Expected " + Integer.toString(output.asInteger("warningCount")) + " warnings, but found " + Integer.toString(wc) + ".");
|
||||
if (output.has("infoCount"))
|
||||
Assertions.assertEquals(output.get("infoCount").getAsInt(), hc, "Expected " + Integer.toString(output.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + ".");
|
||||
Assertions.assertEquals(output.asInteger("infoCount"), hc, "Expected " + Integer.toString(output.asInteger("infoCount")) + " hints, but found " + Integer.toString(hc) + ".");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
-------------------------------------------------------------------------------------
|
||||
{"hierarchical" : false, "valueSet" :{
|
||||
"resourceType" : "ValueSet",
|
||||
"compose" : {
|
||||
"inactive" : true,
|
||||
"include" : [{
|
||||
"system" : "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
|
||||
"concept" : [{
|
||||
"code" : "LL"
|
||||
},
|
||||
{
|
||||
"code" : "HH"
|
||||
},
|
||||
{
|
||||
"code" : "L",
|
||||
"display" : "Extra Low"
|
||||
},
|
||||
{
|
||||
"code" : "H"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}}####
|
||||
e: {
|
||||
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"hierarchical" : false, "valueSet" :{
|
||||
"resourceType" : "ValueSet",
|
||||
"compose" : {
|
||||
"inactive" : true,
|
||||
"include" : [{
|
||||
"system" : "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
|
||||
"concept" : [{
|
||||
"code" : "LL"
|
||||
},
|
||||
{
|
||||
"code" : "HH"
|
||||
},
|
||||
{
|
||||
"code" : "L",
|
||||
"display" : "Extra Low"
|
||||
},
|
||||
{
|
||||
"code" : "H",
|
||||
"display" : "higher"
|
||||
},
|
||||
{
|
||||
"code" : "P"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}}####
|
||||
e: {
|
||||
"error" : "Cannot invoke \"org.hl7.fhir.r5.terminologies.client.ITerminologyClient.expandValueset(org.hl7.fhir.r5.model.ValueSet, org.hl7.fhir.r5.model.Parameters, java.util.Map)\" because the return value of \"org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.getClient()\" is null"
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
Loading…
Reference in New Issue