Update Comparers for annotating models with version differences

This commit is contained in:
Grahame Grieve 2023-08-14 15:33:24 +10:00
parent 1d4eefa62b
commit 9cde582732
7 changed files with 516 additions and 138 deletions

View File

@ -11,6 +11,7 @@ import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState;
import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CapabilityStatement;
@ -52,7 +53,8 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
private ChangeAnalysisState changedContent = ChangeAnalysisState.Unknown;
private ChangeAnalysisState changedContentInterpretation = ChangeAnalysisState.Unknown;
protected Map<String, StructuralMatch<String>> metadata = new HashMap<>();
protected Map<String, StructuralMatch<String>> metadata = new HashMap<>();
private List<String> chMetadataFields;
public CanonicalResourceComparison(T left, T right) {
super(left.getId(), right.getId());
@ -133,8 +135,9 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
changedContentInterpretation = updateState(state, changedContentInterpretation);
}
public void updatedMetadataState(boolean state) {
public void updatedMetadataState(boolean state, List<String> chMetadataFields) {
changedMetadata = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedMetadata);
this.chMetadataFields = chMetadataFields;
}
public void updateDefinitionsState(boolean state) {
@ -149,6 +152,27 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
changedContentInterpretation = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedContentInterpretation);
}
public boolean anyUpdates() {
return changedMetadata.noteable() || changedDefinitions.noteable() || changedContent.noteable() || changedContentInterpretation.noteable();
}
public ChangeAnalysisState getChangedMetadata() {
return changedMetadata;
}
public ChangeAnalysisState getChangedDefinitions() {
return changedDefinitions;
}
public ChangeAnalysisState getChangedContent() {
return changedContent;
}
public ChangeAnalysisState getChangedContentInterpretation() {
return changedContentInterpretation;
}
@Override
protected String toTable() {
String s = "";
@ -198,30 +222,76 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
}
return (bc.length() == 0 ? "" : "Error Checking: "+bc.toString()+"; ")+ "Changed: "+b.toString();
}
public String getMetadataFieldsAsText() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
if (chMetadataFields != null) {
for (String s : chMetadataFields) {
b.append(s);
}
}
return b.toString();
}
}
public CanonicalResourceComparer(ComparisonSession session) {
super(session);
}
protected boolean compareMetadata(CanonicalResource left, CanonicalResource right, Map<String, StructuralMatch<String>> comp, CanonicalResourceComparison<? extends CanonicalResource> res) {
protected boolean compareMetadata(CanonicalResource left, CanonicalResource right, Map<String, StructuralMatch<String>> comp, CanonicalResourceComparison<? extends CanonicalResource> res, List<String> changes) {
var changed = false;
changed = comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res) || changed;
if (session.getForVersion() == null) {
changed = comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res) || changed;
if (comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res)) {
changed = true;
changes.add("url");
}
changed = comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res) || changed;
if (session.getForVersion() == null) {
changed = comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res) || changed;
if (comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res)) {
changed = true;
changes.add("version");
}
}
if (comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("name");
}
if (comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("title");
}
if (comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("status");
}
if (comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res)) {
changed = true;
changes.add("experimental");
}
if (session.getForVersion() == null) {
if (comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("date");
}
}
if (comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("publisher");
}
if (comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res)) {
changed = true;
changes.add("description");
}
if (comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res)) {
changed = true;
changes.add("purpose");
}
if (comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res)) {
changed = true;
changes.add("copyright");
}
if (compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction())) {
changed = true;
changes.add("jurisdiction");
}
changed = comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res) || changed;
changed = comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res) || changed;
changed = comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res) || changed;
changed = compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction()) || changed;
return changed;
}
@ -374,6 +444,41 @@ public abstract class CanonicalResourceComparer extends ResourceComparer {
return match.isDifferent();
}
@SuppressWarnings("rawtypes")
protected boolean comparePrimitivesWithTracking(String name, PrimitiveType l, PrimitiveType r, Map<String, StructuralMatch<String>> comp, IssueSeverity level, CanonicalResourceComparison<? extends CanonicalResource> res, Base parent, String version) {
StructuralMatch<String> 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));
VersionComparisonAnnotation.markAdded(r, version);
} else if (r.isEmpty()) {
match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "Removed the item '"+l.primitiveValue()+"'", fhirType()+"."+name));
VersionComparisonAnnotation.markDeleted(parent, version, name, l);
} 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));
VersionComparisonAnnotation.markAdded(r, version);
} else if (!r.hasValue()) {
match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "No Value on Right", fhirType()+"."+name));
VersionComparisonAnnotation.markDeleted(parent, version, name, l);
} else if (l.getValue().equals(r.getValue())) {
match = new StructuralMatch<>(l.primitiveValue(), r.primitiveValue(), null);
} else {
VersionComparisonAnnotation.markChanged(r, version);
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));
}
}
if (comp != null) {
comp.put(name, match);
}
return match.isDifferent();
}
protected abstract String fhirType();
public XhtmlNode renderMetadata(CanonicalResourceComparison<? extends CanonicalResource> comparison, String id, String prefix) throws FHIRException, IOException {

View File

@ -114,7 +114,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
cs1.setStatus(left.getStatus());
cs1.setDate(new Date());
compareMetadata(left, right, res.getMetadata(), res);
compareMetadata(left, right, res.getMetadata(), res, new ArrayList<>());
comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.ERROR, res);
compareCanonicalList("instantiates", left.getInstantiates(), right.getInstantiates(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getInstantiates(), cs1.getInstantiates());
compareCanonicalList("imports", left.getImports(), right.getImports(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getImports(), cs1.getImports());

View File

@ -10,7 +10,9 @@ import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
@ -31,6 +33,8 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
public class CodeSystemComparison extends CanonicalResourceComparison<CodeSystem> {
private StructuralMatch<PropertyComponent> properties;
private StructuralMatch<CodeSystemFilterComponent> filters;
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) {
@ -46,6 +50,14 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return combined;
}
public StructuralMatch<PropertyComponent> getProperties() {
return properties;
}
public StructuralMatch<CodeSystemFilterComponent> getFilters() {
return filters;
}
@Override
protected String abbreviation() {
return "cs";
@ -87,7 +99,6 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
if (right == null)
throw new DefinitionException("No CodeSystem provided (right)");
CodeSystemComparison res = new CodeSystemComparison(left, right);
session.identify(res);
CodeSystem cs = new CodeSystem();
@ -119,18 +130,29 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
cs1.setDate(new Date());
cs1.getProperty().addAll(cs.getProperty());
boolean ch = compareMetadata(left, right, res.getMetadata(), res);
ch = comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res) || ch;
ch = comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
res.updatedMetadataState(ch);
List<String> chMetadata = new ArrayList<>();
boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
if (comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res)) {
ch = true;
chMetadata.add("versionNeeded");
}
if (comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("compositional");
}
res.updatedMetadataState(ch, chMetadata);
ch = false;
ch = comparePrimitives("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res);
ch = comparePrimitivesWithTracking("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res, right, session.getForVersion()) || ch;
ch = comparePrimitivesWithTracking("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res, right, session.getForVersion()) || ch;
ch = comparePrimitivesWithTracking("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res, right, session.getForVersion());
ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept") || ch;
ch = compareProperties(left.getProperty(), right.getProperty(), res.getProperties(), res.getUnion().getProperty(), res.getIntersection().getProperty(), res.getUnion(), res.getIntersection(), res, "CodeSystem.property", right) || ch;
ch = compareFilters(left.getFilter(), right.getFilter(), res.getFilters(), res.getUnion().getFilter(), res.getIntersection().getFilter(), res.getUnion(), res.getIntersection(), res, "CodeSystem.filter", right) || ch;
ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept", right) || ch;
res.updateDefinitionsState(ch);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -161,10 +183,81 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return null;
}
private boolean compareProperties(List<PropertyComponent> left, List<PropertyComponent> right, StructuralMatch<PropertyComponent> combined,
List<PropertyComponent> union, List<PropertyComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<PropertyComponent> matchR = new ArrayList<>();
for (PropertyComponent l : left) {
PropertyComponent r = findInList(right, l);
if (r == null) {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.PropertyComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
PropertyComponent cdM = merge(l, r, res);
PropertyComponent cdI = intersect(l, r, res);
union.add(cdM);
intersection.add(cdI);
StructuralMatch<PropertyComponent> sm = new StructuralMatch<CodeSystem.PropertyComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
}
}
for (PropertyComponent r : right) {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.PropertyComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;
}
private boolean compareFilters(List<CodeSystemFilterComponent> left, List<CodeSystemFilterComponent> right, StructuralMatch<CodeSystemFilterComponent> combined,
List<CodeSystemFilterComponent> union, List<CodeSystemFilterComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<CodeSystemFilterComponent> matchR = new ArrayList<>();
for (CodeSystemFilterComponent l : left) {
CodeSystemFilterComponent r = findInList(right, l);
if (r == null) {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
CodeSystemFilterComponent cdM = merge(l, r, res);
CodeSystemFilterComponent cdI = intersect(l, r, res);
union.add(cdM);
intersection.add(cdI);
StructuralMatch<CodeSystemFilterComponent> sm = new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
}
}
for (CodeSystemFilterComponent r : right) {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.CodeSystemFilterComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;
}
private boolean compareConcepts(List<ConceptDefinitionComponent> left, List<ConceptDefinitionComponent> right, StructuralMatch<ConceptDefinitionComponent> combined,
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path) {
boolean result = false;
List<ConceptDefinitionComponent> union, List<ConceptDefinitionComponent> intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path, Base parent) {
boolean def = false;
List<ConceptDefinitionComponent> matchR = new ArrayList<>();
for (ConceptDefinitionComponent l : left) {
ConceptDefinitionComponent r = findInList(right, l);
@ -172,6 +265,7 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
union.add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path)));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
} else {
matchR.add(r);
ConceptDefinitionComponent cdM = merge(l, r, csU.getProperty(), res);
@ -179,12 +273,12 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
union.add(cdM);
intersection.add(cdI);
StructuralMatch<ConceptDefinitionComponent> sm = new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(l, r);
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res)) {
result = true;
if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res, parent)) {
def = true;
}
combined.getChildren().add(sm);
if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept")) {
result = true;
if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept", r)) {
def = true;
}
}
}
@ -192,11 +286,22 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
if (!matchR.contains(r)) {
union.add(r);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
combined.getChildren().add(new StructuralMatch<CodeSystem.ConceptDefinitionComponent>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return result;
return def;
}
private CodeSystemFilterComponent findInList(List<CodeSystemFilterComponent> list, CodeSystemFilterComponent item) {
for (CodeSystemFilterComponent t : list) {
if (t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private ConceptDefinitionComponent findInList(List<ConceptDefinitionComponent> list, ConceptDefinitionComponent item) {
for (ConceptDefinitionComponent t : list) {
@ -207,27 +312,58 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return null;
}
private boolean compare(List<ValidationMessage> msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res) {
private PropertyComponent findInList(List<PropertyComponent> list, PropertyComponent item) {
for (PropertyComponent t : list) {
if (t.getCode().equals(item.getCode())) {
return t;
}
}
return null;
}
private boolean compare(List<ValidationMessage> msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res) || result;
result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res) || result;
result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res, parent, l.getDisplayElement(), r.getDisplayElement()) || result;
result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res, parent, l.getDefinitionElement(), r.getDefinitionElement()) || result;
// todo: designations, properties
return result;
}
private boolean compareStrings(String path, List<ValidationMessage> msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res) {
private boolean compare(List<ValidationMessage> msgs, PropertyComponent l, PropertyComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getUri(), r.getUri(), "uri", IssueSeverity.WARNING, res, parent, l.getUriElement(), r.getUriElement()) || result;
result = compareStrings(path, msgs, l.hasType() ? l.getType().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
return result;
}
private boolean compare(List<ValidationMessage> msgs, CodeSystemFilterComponent l, CodeSystemFilterComponent r, String path, CodeSystemComparison res, Base parent) {
boolean result = false;
result = compareStrings(path, msgs, l.getDescription(), r.getDescription(), "description", IssueSeverity.WARNING, res, parent, l.getDescriptionElement(), r.getDescriptionElement()) || result;
// todo: repeating
// result = compareStrings(path, msgs, l.hasOperator() ? l.getOperator().toCode() : null, r.hasType() ? r.getType().toCode() : null, "type", IssueSeverity.ERROR, res, parent, l.getTypeElement(), r.getTypeElement()) || result;
result = compareStrings(path, msgs, l.getValue(), r.getValue(), "value", IssueSeverity.WARNING, res, parent, l.getValueElement(), r.getValueElement()) || result;
return result;
}
private boolean compareStrings(String path, List<ValidationMessage> msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res, Base parent, Base l, Base r) {
if (!Utilities.noString(right)) {
if (Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" added", path));
msgs.add(vmI(level, "Value for "+name+" added", path));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
return true;
} else if (!left.equals(right)) {
if (level != IssueSeverity.NULL) {
res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level));
}
msgs.add(vmI(level, name+" changed from left to right", path));
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
return true;
}
} else if (!Utilities.noString(left)) {
msgs.add(vmI(level, "Value for "+name+" removed", path));
VersionComparisonAnnotation.markDeleted(parent, session.getForVersion(), "concept", l);
return true;
}
return false;
@ -246,6 +382,23 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return cd;
}
private PropertyComponent merge(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
PropertyComponent cd = l.copy();
if (!l.hasDescription() && r.hasDescription()) {
cd.setDescription(r.getDescription());
}
return cd;
}
private CodeSystemFilterComponent merge(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
CodeSystemFilterComponent cd = l.copy();
if (!l.hasDescription() && r.hasDescription()) {
cd.setDescription(r.getDescription());
}
return cd;
}
private ConceptDefinitionComponent intersect(ConceptDefinitionComponent l, ConceptDefinitionComponent r, CodeSystemComparison res) {
ConceptDefinitionComponent cd = l.copy();
if (l.hasDisplay() && !r.hasDisplay()) {
@ -259,6 +412,22 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
return cd;
}
private PropertyComponent intersect(PropertyComponent l, PropertyComponent r, CodeSystemComparison res) {
PropertyComponent cd = l.copy();
if (l.hasDescription() && !r.hasDescription()) {
cd.setDescription(null);
}
return cd;
}
private CodeSystemFilterComponent intersect(CodeSystemFilterComponent l, CodeSystemFilterComponent r, CodeSystemComparison res) {
CodeSystemFilterComponent cd = l.copy();
if (l.hasDescription() && !r.hasDescription()) {
cd.setDescription(null);
}
return cd;
}
private void mergeDesignations(ConceptDefinitionComponent cd, ConceptDefinitionComponent l, ConceptDefinitionComponent r) {
for (ConceptDefinitionDesignationComponent td : l.getDesignation()) {
if (hasDesignation(td, r.getDesignation())) {
@ -341,7 +510,8 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
private ConceptPropertyComponent findRightProp(List<ConceptPropertyComponent> rightProperties, ConceptPropertyComponent lp, CodeSystemComparison res) {
for (ConceptPropertyComponent p : rightProperties) {
if (res.getPropMap().get(p.getCode()).equals(lp.getCode())) {
String rp = res.getPropMap().get(p.getCode());
if (rp != null && rp.equals(lp.getCode())) {
return p;
}
}

View File

@ -107,9 +107,9 @@ public class ComparisonSession {
throw new FHIRException("Unable to compare resources of type "+left.fhirType()+" and "+right.fhirType());
}
} catch (Throwable e) {
if (debug) {
// if (debug) {
e.printStackTrace();
}
// }
ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right, e);
compares.put(key, csc);
return csc;

View File

@ -5,10 +5,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceComparison;
import org.hl7.fhir.r5.comparison.ValueSetComparer.ValueSetComparison;
import org.hl7.fhir.r5.conformance.profile.BindingResolution;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
@ -17,6 +19,7 @@ import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.ElementDefinition;
@ -143,20 +146,42 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
sd1.setStatus(left.getStatus());
sd1.setDate(new Date());
compareMetadata(left, right, res.getMetadata(), res);
comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("abstract", left.getAbstractElement(), right.getAbstractElement(), res.getMetadata(), IssueSeverity.WARNING, res);
comparePrimitives("type", left.getTypeElement(), right.getTypeElement(), res.getMetadata(), IssueSeverity.ERROR, res);
comparePrimitives("baseDefinition", left.getBaseDefinitionElement(), right.getBaseDefinitionElement(), res.getMetadata(), IssueSeverity.ERROR, res);
List<String> chMetadata = new ArrayList<>();
boolean ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
if (comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("fhirVersion");
}
if (comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("kind");
}
if (comparePrimitives("abstract", left.getAbstractElement(), right.getAbstractElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("abstract");
}
res.updatedMetadataState(ch, chMetadata);
ch = false;
ch = comparePrimitives("type", left.getTypeElement(), right.getTypeElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
ch = comparePrimitives("baseDefinition", left.getBaseDefinitionElement(), right.getBaseDefinitionElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch;
if (left.getType().equals(right.getType())) {
DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left);
DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right);
DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left, false);
DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right, false);
StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current()));
compareElements(res, sm, ln.path(), null, ln, rn);
res.combined = sm;
}
if (left.getType().equals(right.getType())) {
DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left, true);
DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right, true);
StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current()));
ch = compareElements(res, sm, ln.path(), null, ln, rn) || ch;
// we don't preserve the differences - we only want the annotations
}
res.updateDefinitionsState(ch);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -173,12 +198,14 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
throw new DefinitionException("StructureDefinition snapshot is empty ("+name+": "+sd.getName()+")");
}
private void compareElements(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
private boolean compareElements(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException {
assert(path != null);
assert(left != null);
assert(right != null);
assert(left.path().equals(right.path()));
boolean def = false;
if (session.isDebug()) {
System.out.println("Compare elements at "+path);
}
@ -196,7 +223,6 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
if (sliceName != null)
subset.setSliceName(sliceName);
subset.getRepresentation().addAll(left.current().getRepresentation()); // can't be bothered even testing this one
subset.setDefaultValue(left.current().getDefaultValue());
subset.setMeaningWhenMissing(left.current().getMeaningWhenMissing());
@ -205,10 +231,20 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
// descriptive properties from ElementDefinition - merge them:
subset.setLabel(mergeText(comp, res, path, "label", left.current().getLabel(), right.current().getLabel(), false));
comparePrimitivesWithTracking("label", left.current().getLabelElement(), right.current().getLabelElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion());
subset.setShort(mergeText(comp, res, path, "short", left.current().getShort(), right.current().getShort(), false));
def = comparePrimitivesWithTracking("short", left.current().getShortElement(), right.current().getShortElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setDefinition(mergeText(comp, res, path, "definition", left.current().getDefinition(), right.current().getDefinition(), false));
def = comparePrimitivesWithTracking("definition", left.current().getDefinitionElement(), right.current().getDefinitionElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setComment(mergeText(comp, res, path, "comments", left.current().getComment(), right.current().getComment(), false));
def = comparePrimitivesWithTracking("comment", left.current().getCommentElement(), right.current().getCommentElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.setRequirements(mergeText(comp, res, path, "requirements", left.current().getRequirements(), right.current().getRequirements(), false));
def = comparePrimitivesWithTracking("requirements", left.current().getRequirementsElement(), right.current().getRequirementsElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
subset.getCode().addAll(mergeCodings(left.current().getCode(), right.current().getCode()));
subset.getAlias().addAll(mergeStrings(left.current().getAlias(), right.current().getAlias()));
subset.getMapping().addAll(mergeMappings(left.current().getMapping(), right.current().getMapping()));
@ -220,14 +256,18 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
}
subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport());
def = comparePrimitivesWithTracking("mustSupport", left.current().getMustSupportElement(), right.current().getMustSupportElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
ElementDefinition superset = subset.copy();
def = comparePrimitivesWithTracking("min", left.current().getMinElement(), right.current().getMinElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
def = comparePrimitivesWithTracking("max", left.current().getMaxElement(), right.current().getMaxElement(), null, IssueSeverity.INFORMATION, comp, right.current(), session.getForVersion()) || def;
// compare and intersect
int leftMin = left.current().getMin();
int rightMin = right.current().getMin();
int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(left.current().getMax());
int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(right.current().getMax());
int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(left.current().getMax(), -1);
int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Utilities.parseInt(right.current().getMax(), -1);
checkMinMax(comp, res, path, leftMin, rightMin, leftMax, rightMax);
superset.setMin(unionMin(leftMin, rightMin));
@ -252,7 +292,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
comp.getUnion().getSnapshot().getElement().add(superset);
// add the children
compareChildren(comp, res, path, left, right);
def = compareChildren(comp, res, path, left, right) || def;
//
// // now process the slices
// if (left.current().hasSlicing() || right.current().hasSlicing()) {
@ -312,10 +352,13 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
//
// // TODO Auto-generated method stub
// return null;
return def;
}
private void compareChildren(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError {
private boolean compareChildren(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, String path, DefinitionNavigator left, DefinitionNavigator right) 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
@ -336,7 +379,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
matchR.add(r);
StructuralMatch<ElementDefinitionNode> sm = new StructuralMatch<ElementDefinitionNode>(new ElementDefinitionNode(l.getStructure(), l.current()), new ElementDefinitionNode(r.getStructure(), r.current()));
res.getChildren().add(sm);
compareElements(comp, sm, l.path(), null, l, r);
def = compareElements(comp, sm, l.path(), null, l, r) || def;
}
}
for (DefinitionNavigator r : rc) {
@ -345,6 +388,7 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
res.getChildren().add(new StructuralMatch<ElementDefinitionNode>(vmI(IssueSeverity.INFORMATION, "Added this element", path), new ElementDefinitionNode(r.getStructure(), r.current())));
}
}
return def;
}
private DefinitionNavigator findInList(List<DefinitionNavigator> rc, DefinitionNavigator l) {
@ -406,9 +450,6 @@ public class ProfileComparer extends CanonicalResourceComparer implements Profil
right = stripLinks(right);
if (left.equalsIgnoreCase(right))
return left;
if (path != null) {
vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+": '"+left+"' vs '"+right+"'", path, comp.getMessages(), res.getMessages());
}
return "left: "+left+"; right: "+right;
}

View File

@ -124,18 +124,26 @@ public class ValueSetComparer extends CanonicalResourceComparer {
vs1.setStatus(left.getStatus());
vs1.setDate(new Date());
var ch = compareMetadata(left, right, res.getMetadata(), res);
List<String> chMetadata = new ArrayList<>();
var ch = compareMetadata(left, right, res.getMetadata(), res, chMetadata);
var def = false;
ch = comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
if (comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("immutable");
}
if (left.hasCompose() || right.hasCompose()) {
ch = comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch;
if (comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res)) {
ch = true;
chMetadata.add("compose.lockedDate");
}
def = comparePrimitives("compose.inactive", left.getCompose().getInactiveElement(), right.getCompose().getInactiveElement(), res.getMetadata(), IssueSeverity.WARNING, res) || def;
}
res.updatedMetadataState(ch);
res.updatedMetadataState(ch, chMetadata);
def = compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()) || def;
res.updateDefinitionsState(def);
compareExpansions(left, right, res);
VersionComparisonAnnotation.annotate(right, session.getForVersion(), res);
return res;
}
@ -149,9 +157,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getInclude().add(l);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed Include", "ValueSet.compose.include")));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "include", l);
}
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "include", l);
} else {
matchR.add(r);
ConceptSetComponent csM = new ConceptSetComponent();
@ -168,9 +174,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getInclude().add(r);
res.updateContentState(true);
res.getIncludes().getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added Include", "ValueSet.compose.include"), r));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -247,6 +251,9 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getValueSet().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed ValueSet", "ValueSet.compose.include.valueSet")));
if (session.isAnnotate()) {
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "valueset", l);
}
} else {
matchVSR.add(r);
if (l.getValue().equals(r.getValue())) {
@ -260,7 +267,11 @@ public class ValueSetComparer extends CanonicalResourceComparer {
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"));
combined.getChildren().add(sm);
combined.getChildren().add(sm);
if (session.isAnnotate()) {
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
}
}
}
@ -268,7 +279,8 @@ 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.INFORMATION, "Add ValueSet", "ValueSet.compose.include.valueSet"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -279,6 +291,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getConcept().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this Concept", "ValueSet.compose.include.concept")));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "concept", l);
} else {
matchCR.add(r);
if (l.getCode().equals(r.getCode())) {
@ -297,6 +310,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
combined.getChildren().add(sm);
res.updateContentState(true);
compareConcepts(l, r, sm, null, null);
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
}
}
@ -304,7 +318,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.INFORMATION, "Added this Concept", "ValueSet.compose.include.concept"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
@ -315,6 +330,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
union.getFilter().add(l);
res.updateContentState(true);
combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", "ValueSet.compose.include.filter")));
VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "filter", l);
} else {
matchFR.add(r);
if (l.getProperty().equals(r.getProperty()) && l.getOp().equals(r.getOp())) {
@ -325,7 +341,8 @@ public class ValueSetComparer extends CanonicalResourceComparer {
StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r);
combined.getChildren().add(sm);
if (!compareFilters(l, r, sm, cu, ci)) {
res.updateContentState(true);
res.updateContentState(true);
VersionComparisonAnnotation.markChanged(r, session.getForVersion());
}
} else {
union.getFilter().add(l);
@ -341,7 +358,8 @@ 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.INFORMATION, "Added this item", "ValueSet.compose.include.filter"), r));
VersionComparisonAnnotation.markAdded(r, session.getForVersion());
}
}
return def;

View File

@ -1,69 +1,80 @@
package org.hl7.fhir.r5.comparison;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.CanonicalResourceComparison;
import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class VersionComparisonAnnotation {
public enum AnotationType {
Added, Changed, Deleted;
NoChange, Added, Changed, Deleted;
}
public static final String USER_DATA_NAME = "version-annotation";
private AnotationType type;
// private String comment;
// private String link;
private Map<String, List<Base>> deletedChildren;
private String version;
private CanonicalResourceComparison<? extends CanonicalResource> comp;
private VersionComparisonAnnotation(AnotationType type, String version) {
super();
this.type = type;
this.version = version;
}
//
// private VersionComparisonAnnotation(AnotationType type, String comment) {
// super();
// this.type = type;
// this.comment = comment;
// }
// private VersionComparisonAnnotation(AnotationType type, String comment, String link) {
// super();
// this.type = type;
// this.comment = comment;
// this.link = link;
// }
//
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);
vca.comp = comp;
base.setUserData(USER_DATA_NAME, vca);
}
}
public static void markAdded(Base focus, String version) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version));
if (version != null) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version));
}
}
public static void markChanged(Base focus, String version) {
if (version != null) {
focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Changed, version));
}
}
public static void markDeleted(Base parent, String version, String name, Base other) {
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);
parent.setUserData(USER_DATA_NAME, vca);
if (version != 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);
parent.setUserData(USER_DATA_NAME, vca);
}
if (vca.deletedChildren == null) {
vca.deletedChildren = new HashMap<>();
}
if (!vca.deletedChildren.containsKey(name)) {
vca.deletedChildren.put(name, new ArrayList<>());
}
other.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Deleted, version));
vca.deletedChildren.get(name).add(other);
}
if (vca.deletedChildren == null) {
vca.deletedChildren = new HashMap<>();
}
if (!vca.deletedChildren.containsKey(name)) {
vca.deletedChildren.put(name, new ArrayList<>());
}
other.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Deleted, version));
vca.deletedChildren.get(name).add(other);
}
public AnotationType getType() {
@ -73,19 +84,6 @@ public class VersionComparisonAnnotation {
this.type = type;
}
// public String getComment() {
// return comment;
// }
// public void setComment(String comment) {
// this.comment = comment;
// }
// public String getLink() {
// return link;
// }
// public void setLink(String link) {
// this.link = link;
// }
public static XhtmlNode render(Base b, XhtmlNode x) {
if (b.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME);
@ -98,20 +96,57 @@ public class VersionComparisonAnnotation {
private XhtmlNode render(XhtmlNode x) {
switch (type) {
case Added:
XhtmlNode span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version);
span.img("icon-change-add.png", "icon");
span.tx(" Added:");
return x;
XhtmlNode spanOuter = x.span("border: solid 1px #dddddd; margin: 2px; padding: 2px", null);
XhtmlNode spanInner = spanOuter.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 spanOuter;
case Changed:
span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been changed since version "+version);
span.img("icon-change-edit.png", "icon");
span.tx(" Changed:");
return x;
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.img("icon-change-edit.png", "icon");
spanInner.tx(" Changed:");
return spanOuter;
case Deleted:
span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since version "+version);
span.img("icon-change-remove.png", "icon");
span.tx(" Removed:");
return x.strikethrough();
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.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return spanOuter.strikethrough();
default:
return x;
}
}
public static XhtmlNode renderDiv(Base b, XhtmlNode x) {
if (b.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME);
return self.renderDiv(x);
} else {
return x;
}
}
private XhtmlNode renderDiv(XhtmlNode x) {
switch (type) {
case Added:
XhtmlNode divOuter = x.div("border: solid 1px #dddddd; margin: 2px; padding: 2px");
XhtmlNode 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.img("icon-change-add.png", "icon");
spanInner.tx(" Added:");
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.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.img("icon-change-remove.png", "icon");
spanInner.tx(" Removed:");
return divOuter.strikethrough();
default:
return x;
}
@ -142,5 +177,14 @@ public class VersionComparisonAnnotation {
}
return result;
}
public static CanonicalResourceComparison<? extends CanonicalResource> artifactComparison(Base base) {
if (base.hasUserData(USER_DATA_NAME)) {
VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME);
return self.comp;
} else {
return null;
}
}
}