Update Comparers for annotating models with version differences
This commit is contained in:
parent
1d4eefa62b
commit
9cde582732
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue