diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java index e615be219..d3d80cd24 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java @@ -11,7 +11,13 @@ import java.util.Set; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -26,21 +32,18 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { public abstract class CanonicalResourceComparison extends ResourceComparison { - protected T left; protected T right; protected T union; protected T intersection; protected Map> metadata = new HashMap<>(); - public CanonicalResourceComparison(T left, T right) { super(left.getId(), right.getId()); this.left = left; this.right = right; } - public T getLeft() { return left; } @@ -53,12 +56,10 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { return union; } - public T getIntersection() { return intersection; } - public Map> getMetadata() { return metadata; } @@ -67,17 +68,14 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { this.left = left; } - public void setRight(T right) { this.right = right; } - public void setUnion(T union) { this.union = union; } - public void setIntersection(T intersection) { this.intersection = intersection; } @@ -96,7 +94,6 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { s = s + ""+outcomeSummary()+""; return ""+s+"\r\n"; } - @Override protected void countMessages(MessageCounts cnts) { @@ -111,8 +108,8 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { } protected void compareMetadata(CanonicalResource left, CanonicalResource right, Map> comp, CanonicalResourceComparison res) { - comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.INFORMATION, res); + comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res); + comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res); comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res); comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res); comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res); @@ -122,6 +119,122 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res); comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res); comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res); + compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction()); + } + + protected void compareCodeableConceptList(String name, List left, List right, Map> comp, IssueSeverity level, CanonicalResourceComparison res, List union, List intersection ) { + List matchR = new ArrayList<>(); + StructuralMatch combined = new StructuralMatch(); + for (CodeableConcept l : left) { + CodeableConcept r = findCodeableConceptInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(gen(l), vm(IssueSeverity.INFORMATION, "Removed the item '"+gen(l)+"'", fhirType()+"."+name, res.getMessages()))); + } else { + matchR.add(r); + union.add(r); + intersection.add(r); + StructuralMatch sm = new StructuralMatch(gen(l), gen(r)); + combined.getChildren().add(sm); + } + } + for (CodeableConcept r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION, "Added the item '"+gen(r)+"'", fhirType()+"."+name, res.getMessages()), gen(r))); + } + } + comp.put(name, combined); + } + + + private CodeableConcept findCodeableConceptInList(List list, CodeableConcept item) { + for (CodeableConcept t : list) { + if (t.matches(item)) { + return t; + } + } + return null; + } + + protected String gen(CodeableConcept cc) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (Coding c : cc.getCoding()) { + b.append(gen(c)); + } + return b.toString(); + } + + protected String gen(Coding c) { + return c.getSystem()+(c.hasVersion() ? "|"+c.getVersion() : "")+"#"+c.getCode(); + } + + protected void compareCanonicalList(String name, List left, List right, Map> comp, IssueSeverity level, CanonicalResourceComparison res, List union, List intersection ) { + List matchR = new ArrayList<>(); + StructuralMatch combined = new StructuralMatch(); + for (CanonicalType l : left) { + CanonicalType r = findCanonicalInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION, "Removed the item '"+l.getValue()+"'", fhirType()+"."+name, res.getMessages()))); + } else { + matchR.add(r); + union.add(r); + intersection.add(r); + StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue()); + combined.getChildren().add(sm); + } + } + for (CanonicalType r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION, "Added the item '"+r.getValue()+"'", fhirType()+"."+name, res.getMessages()), r.getValue())); + } + } + comp.put(name, combined); + } + + private CanonicalType findCanonicalInList(List list, CanonicalType item) { + for (CanonicalType t : list) { + if (t.getValue().equals(item.getValue())) { + return t; + } + } + return null; + } + + protected void compareCodeList(String name, List left, List right, Map> comp, IssueSeverity level, CanonicalResourceComparison res, List union, List intersection ) { + List matchR = new ArrayList<>(); + StructuralMatch combined = new StructuralMatch(); + for (CodeType l : left) { + CodeType r = findCodeInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l.getValue(), vm(IssueSeverity.INFORMATION, "Removed the item '"+l.getValue()+"'", fhirType()+"."+name, res.getMessages()))); + } else { + matchR.add(r); + union.add(r); + intersection.add(r); + StructuralMatch sm = new StructuralMatch(l.getValue(), r.getValue()); + combined.getChildren().add(sm); + } + } + for (CodeType r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION, "Added the item '"+r.getValue()+"'", fhirType()+"."+name, res.getMessages()), r.getValue())); + } + } + comp.put(name, combined); + } + + private CodeType findCodeInList(List list, CodeType item) { + for (CodeType t : list) { + if (t.getValue().equals(item.getValue())) { + return t; + } + } + return null; } @SuppressWarnings("rawtypes") @@ -130,9 +243,9 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { 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 this item", fhirType()+"."+name)); + match = new StructuralMatch<>(null, r.primitiveValue(), vmI(IssueSeverity.INFORMATION, "Added the item '"+r.primitiveValue()+"'", fhirType()+"."+name)); } else if (r.isEmpty()) { - match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "Removed this item", fhirType()+"."+name)); + match = new StructuralMatch<>(l.primitiveValue(), null, vmI(IssueSeverity.INFORMATION, "Removed the item '"+l.primitiveValue()+"'", fhirType()+"."+name)); } else if (!l.hasValue() && !r.hasValue()) { match = new StructuralMatch<>(null, null, vmI(IssueSeverity.INFORMATION, "No Value", fhirType()+"."+name)); } else if (!l.hasValue()) { @@ -191,6 +304,11 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { r.getCells().add(missingCell(gen).span(2)); } r.getCells().add(cellForMessages(gen, t.getMessages())); + int i = 0; + for (StructuralMatch c : t.getChildren()) { + addRow(gen, r.getSubRows(), name+"["+i+"]", c); + i++; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CapabilityStatementComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CapabilityStatementComparer.java new file mode 100644 index 000000000..9be261a15 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CapabilityStatementComparer.java @@ -0,0 +1,879 @@ +package org.hl7.fhir.r5.comparison; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +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.BackboneElement; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.ResourceVersionPolicy; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataType; +import org.hl7.fhir.r5.model.Element; +import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; +import org.hl7.fhir.utilities.validation.ValidationMessage.Source; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; +import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; + +public class CapabilityStatementComparer extends CanonicalResourceComparer { + + + public class CapabilityStatementComparison extends CanonicalResourceComparison { + + private StructuralMatch combined; + + public CapabilityStatementComparison(CapabilityStatement left, CapabilityStatement right) { + super(left, right); + combined = new StructuralMatch(); // base + } + + public StructuralMatch getCombined() { + return combined; + } + + @Override + protected String abbreviation() { + return "cps"; + } + + @Override + protected String summary() { + return "CapabilityStatement: "+left.present()+" vs "+right.present(); + } + + @Override + protected String fhirType() { + return "CapabilityStatement"; + } + + @Override + protected void countMessages(MessageCounts cnts) { + super.countMessages(cnts); + combined.countMessages(cnts); + } + } + + public CapabilityStatementComparer(ComparisonSession session) { + super(session); + } + + public CapabilityStatementComparison compare(CapabilityStatement left, CapabilityStatement right) { + if (left == null) + throw new DefinitionException("No CapabilityStatement provided (left)"); + if (right == null) + throw new DefinitionException("No CapabilityStatement provided (right)"); + + + CapabilityStatementComparison res = new CapabilityStatementComparison(left, right); + session.identify(res); + CapabilityStatement cs = new CapabilityStatement(); + res.setUnion(cs); + session.identify(cs); + cs.setName("Union"+left.getName()+"And"+right.getName()); + cs.setTitle("Union of "+left.getTitle()+" And "+right.getTitle()); + cs.setStatus(left.getStatus()); + cs.setDate(new Date()); + + CapabilityStatement cs1 = new CapabilityStatement(); + res.setIntersection(cs1); + session.identify(cs1); + cs1.setName("Intersection"+left.getName()+"And"+right.getName()); + cs1.setTitle("Intersection of "+left.getTitle()+" And "+right.getTitle()); + cs1.setStatus(left.getStatus()); + cs1.setDate(new Date()); + + compareMetadata(left, right, res.getMetadata(), res); + 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()); + comparePrimitives("software.name", left.getSoftware().getNameElement(), right.getSoftware().getNameElement(), res.getMetadata(), IssueSeverity.ERROR, res); + comparePrimitives("software.version", left.getSoftware().getVersionElement(), right.getSoftware().getVersionElement(), res.getMetadata(), IssueSeverity.ERROR, res); + comparePrimitives("software.releaseDate", left.getSoftware().getReleaseDateElement(), right.getSoftware().getReleaseDateElement(), res.getMetadata(), IssueSeverity.ERROR, res); + comparePrimitives("implementation.description", left.getImplementation().getDescriptionElement(), right.getImplementation().getDescriptionElement(), res.getMetadata(), IssueSeverity.ERROR, res); + comparePrimitives("implementation.url", left.getImplementation().getUrlElement(), right.getImplementation().getUrlElement(), res.getMetadata(), IssueSeverity.ERROR, res); + comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.ERROR, res); + compareCodeList("format", left.getFormat(), right.getFormat(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getFormat(), cs1.getFormat()); + compareCodeList("patchFormat", left.getPatchFormat(), right.getPatchFormat(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getPatchFormat(), cs1.getPatchFormat()); + compareCanonicalList("implementationGuide", left.getImplementationGuide(), right.getImplementationGuide(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getImplementationGuide(), cs1.getImplementationGuide()); + + + compareRests(left.getRest(), right.getRest(), res.getCombined(), res.getUnion().getRest(), res.getIntersection().getRest(), res.getUnion(), res.getIntersection(), res, "CapabilityStatement.rest"); + return res; + } + + private void compareRests(List left, List right, StructuralMatch combined, List union, List intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { + List matchR = new ArrayList<>(); + for (CapabilityStatementRestComponent l : left) { + CapabilityStatementRestComponent r = findInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); + } else { + matchR.add(r); + CapabilityStatementRestComponent cdM = merge(l, r, res); + CapabilityStatementRestComponent cdI = intersect(l, r, res); + union.add(cdM); + intersection.add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compare(sm, l, r, path+".where(mode='"+l.getMode()+"')", res); + combined.getChildren().add(sm); + compareRestSecurity(l, r, sm, cdM.getSecurity(), cdI.getSecurity(), csU, csI, res, path+".security"); + compareRestResources(l, r, sm, cdM, cdI, csU, csI, res, path+".resource"); + compareSearchParams(combined, l.getSearchParam(), r.getSearchParam(), path, res, cdM.getSearchParam(), cdI.getSearchParam()); + compareOperations(combined, l.getOperation(), r.getOperation(), path, res, cdM.getOperation(), cdI.getOperation()); + compareItemPropertyList(sm, "compartment", l.getCompartment(), r.getCompartment(), path, res, cdM.getCompartment(), cdI.getCompartment(), IssueSeverity.ERROR); + } + } + for (CapabilityStatementRestComponent r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); + } + } + } + + private CapabilityStatementRestComponent findInList(List list, CapabilityStatementRestComponent item) { + for (CapabilityStatementRestComponent t : list) { + if (t.getMode().equals(item.getMode())) { + return t; + } + } + return null; + } + + private void compare(StructuralMatch sm, CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, String path, CapabilityStatementComparison res) { + compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.WARNING, res); + } + + private void compareRestSecurity(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, StructuralMatch smp, CapabilityStatementRestSecurityComponent merge, CapabilityStatementRestSecurityComponent intersect, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { + CapabilityStatementRestSecurityComponent ls = l.hasSecurity() ? l.getSecurity() : null; + CapabilityStatementRestSecurityComponent rs = r.hasSecurity() ? r.getSecurity() : null; + + StructuralMatch sm = new StructuralMatch(ls, rs); + smp.getChildren().add(sm); + compareBooleans(path, sm.getMessages(), l.getSecurity().getCorsElement(), r.getSecurity().getCorsElement(), "security.cors", IssueSeverity.WARNING, res); + compareStrings(path, sm.getMessages(), l.getSecurity().getDescription(), r.getSecurity().getDescription(), "security.description", IssueSeverity.INFORMATION, res); + compareRestSecurityService(ls, rs, sm, merge, intersect, csU, csI, res, path+".security"); + } + + private void compareRestSecurityService(CapabilityStatementRestSecurityComponent left, CapabilityStatementRestSecurityComponent right, StructuralMatch combined, CapabilityStatementRestSecurityComponent union, CapabilityStatementRestSecurityComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { + List matchR = new ArrayList<>(); + for (CodeableConcept l : left.getService()) { + CodeableConcept r = findInList(right.getService(), l); + if (r == null) { + union.getService().add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); + } else { + matchR.add(r); + CodeableConcept cdM = CodeableConcept.merge(l, r); + CodeableConcept cdI = CodeableConcept.intersect(l, r); + union.getService().add(cdM); + intersection.getService().add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compare(sm, l, r, path, res); + combined.getChildren().add(sm); + } + } + for (CodeableConcept r : right.getService()) { + if (!matchR.contains(r)) { + union.getService().add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); + } + } + } + + + private void compare(StructuralMatch sm, CodeableConcept l, CodeableConcept r, String path, CapabilityStatementComparison res) { + compareStrings(path, sm.getMessages(), l.getText(), r.getText(), "text", IssueSeverity.INFORMATION, res); + List matches = new ArrayList<>(); + for (Coding lc : l.getCoding()) { + boolean m = false; + for (Coding rc : r.getCoding()) { + if (lc.matches(rc)) { + matches.add(rc); + m = true; + } + } + if (!m) { + sm.getMessages().add(vmI(IssueSeverity.INFORMATION, "Value for "+gen(lc)+" removed", path)); + } + } + for (Coding rc : r.getCoding()) { + if (!matches.contains(rc)) { + sm.getMessages().add(vmI(IssueSeverity.INFORMATION, "Value for "+gen(rc)+" added", path)); + } + } + } + + private CodeableConcept findInList(List list, CodeableConcept item) { + for (CodeableConcept t : list) { + if (t.matches(item)) { + return t; + } + } + return null; + } + + private void compareStrings(String path, List msgs, String left, String right, String name, IssueSeverity level, CapabilityStatementComparison res) { + if (!Utilities.noString(right)) { + if (Utilities.noString(left)) { + msgs.add(vmI(level, "Value for "+name+" added", path)); + } 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)); + } + } else if (!Utilities.noString(left)) { + msgs.add(vmI(level, "Value for "+name+" removed", path)); + } + } + + private void compareExpectations(StructuralMatch combined, Element left, Element right, String path, CapabilityStatementComparison res, Element union, Element intersection) { + Extension l = left.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation"); + Extension r = right.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation"); + if (l != null || r != null) { + if (l == null) { + union.addExtension(r.copy()); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this expectation", path), r)); + } else if (r == null) { + union.addExtension(l.copy()); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this expectation", path))); + } else { + StructuralMatch sm = new StructuralMatch(l, r); + combined.getChildren().add(sm); + String ls = l.getValue().primitiveValue(); + String rs = r.getValue().primitiveValue(); + if (ls.equals(rs)) { + union.addExtension(l.copy()); + intersection.addExtension(l.copy()); + } else { + sm.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+".extension('http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation')", "Changed value for expectation: '"+ls+"' vs '"+rs+"'", IssueSeverity.WARNING)); + String lowest = lower(ls, rs) ? ls : rs; + String highest = lower(ls, rs) ? rs : ls; + union.addExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", new CodeType(lowest)); + intersection.addExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", new CodeType(highest)); + } + } + } + } + + private boolean lower(String ls, String rs) { + if (ls.equals("MAY")) { + return true; + } + if (ls.equals("SHALL")) { + return false; + } + if (rs.equals("MAY")) { + return false; + } + if (rs.equals("SHALL")) { + return true; + } + return false; + } + + private void compareBooleans(String path, List msgs, BooleanType left, BooleanType right, String name, IssueSeverity level, CapabilityStatementComparison res) { + if (!right.isEmpty()) { + if (left.isEmpty()) { + msgs.add(vmI(level, "Value for "+name+" added", path)); + } else if (left.getValue() != right.getValue()) { + 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)); + } + } else if (!left.isEmpty()) { + msgs.add(vmI(level, "Value for "+name+" removed", path)); + } + } + + private CapabilityStatementRestComponent merge(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, CapabilityStatementComparison res) { + CapabilityStatementRestComponent cd = l.copy(); + if (!l.hasDocumentation() && r.hasDocumentation()) { + cd.setDocumentation(r.getDocumentation()); + } + if (r.hasSecurity()) { + if (!l.getSecurity().hasCors() && r.getSecurity().hasCors()) { + cd.getSecurity().setCors(r.getSecurity().getCors()); + } + mergeCodeableConcepts(cd.getSecurity().getService(), r.getSecurity().getService()); + if (!l.getSecurity().hasDescription() && r.getSecurity().hasDescription()) { + cd.getSecurity().setDescription(r.getSecurity().getDescription()); + } + } + return cd; + } + + private void mergeCodeableConcepts(List tgt, List src) { + for (CodeableConcept cd : src) { + boolean add = true; + for (CodeableConcept t : tgt) { + if (t.matches(cd)) { + add = false; + } + } + if (add) { + tgt.add(cd.copy()); + } + } + } + + private CapabilityStatementRestComponent intersect(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, CapabilityStatementComparison res) { + CapabilityStatementRestComponent cd = l.copy(); + if (l.hasDocumentation() && !r.hasDocumentation()) { + cd.setDocumentation(null); + } + if (!r.hasSecurity()) { + cd.setSecurity(null); + } else { + if (!r.getSecurity().hasCors()) { + cd.getSecurity().setCorsElement(null); + } + intersectCodeableConcepts(cd.getSecurity().getService(), r.getSecurity().getService()); + if (!r.getSecurity().hasDescription()) { + cd.getSecurity().setDescription(null); + } + } + return cd; + } + + private void intersectCodeableConcepts(List tgt, List src) { + List toRemove = new ArrayList(); + for (CodeableConcept cd : src) { + boolean remove = false; + for (CodeableConcept t : tgt) { + if (t.matches(cd)) { + remove = true; + } + } + if (remove) { + toRemove.add(cd); + } + } + tgt.removeAll(toRemove); + } + + private void compareRestResources(CapabilityStatementRestComponent left, CapabilityStatementRestComponent right, StructuralMatch combined, CapabilityStatementRestComponent union, CapabilityStatementRestComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { + List matchR = new ArrayList<>(); + for (CapabilityStatementRestResourceComponent l : left.getResource()) { + CapabilityStatementRestResourceComponent r = findInList(right.getResource(), l); + if (r == null) { + union.getResource().add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); + } else { + matchR.add(r); + CapabilityStatementRestResourceComponent cdM = mergeRestResource(l, r); + CapabilityStatementRestResourceComponent cdI = intersectRestResource(l, r); + union.getResource().add(cdM); + intersection.getResource().add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compareRestResource(sm, l, r, path, res, cdM, cdI); + combined.getChildren().add(sm); + } + } + for (CapabilityStatementRestResourceComponent r : right.getResource()) { + if (!matchR.contains(r)) { + union.getResource().add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); + } + } + } + + private void compareRestResource(StructuralMatch sm, CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) { + compareStrings(path, sm.getMessages(), l.getProfile(), r.getProfile(), "profile", IssueSeverity.WARNING, res); + // todo: supported profiles + compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); + compareExpectations(sm, l, r, path, res, union, intersection); + compareRestResourceInteractions(sm, l, r, path, res, union, intersection); + compareItemProperty(sm, "versioning", l.getVersioningElement(), r.getVersioningElement(), path, res, union.getVersioningElement(), intersection.getVersioningElement(), IssueSeverity.WARNING); + compareItemProperty(sm, "readHistory", l.getReadHistoryElement(), r.getReadHistoryElement(), path, res, union.getReadHistoryElement(), intersection.getReadHistoryElement(), IssueSeverity.INFORMATION); + compareItemProperty(sm, "updateCreate", l.getUpdateCreateElement(), r.getUpdateCreateElement(), path, res, union.getUpdateCreateElement(), intersection.getUpdateCreateElement(), IssueSeverity.WARNING); + compareItemProperty(sm, "conditionalCreate", l.getConditionalCreateElement(), r.getConditionalCreateElement(), path, res, union.getConditionalCreateElement(), intersection.getConditionalCreateElement(), IssueSeverity.WARNING); + compareItemProperty(sm, "conditionalRead", l.getConditionalReadElement(), r.getConditionalReadElement(), path, res, union.getConditionalReadElement(), intersection.getConditionalReadElement(), IssueSeverity.WARNING); + compareItemProperty(sm, "conditionalUpdate", l.getConditionalUpdateElement(), r.getConditionalUpdateElement(), path, res, union.getConditionalUpdateElement(), intersection.getConditionalUpdateElement(), IssueSeverity.WARNING); + compareItemProperty(sm, "conditionalDelete", l.getConditionalDeleteElement(), r.getConditionalDeleteElement(), path, res, union.getConditionalDeleteElement(), intersection.getConditionalDeleteElement(), IssueSeverity.WARNING); + compareItemPropertyList(sm, "referencePolicy", l.getReferencePolicy(), r.getReferencePolicy(), path, res, union.getReferencePolicy(), intersection.getReferencePolicy(), IssueSeverity.WARNING); + compareItemPropertyList(sm, "searchInclude", l.getSearchInclude(), r.getSearchInclude(), path, res, union.getSearchInclude(), intersection.getSearchInclude(), IssueSeverity.WARNING); + compareItemPropertyList(sm, "searchRevInclude", l.getSearchRevInclude(), r.getSearchRevInclude(), path, res, union.getSearchRevInclude(), intersection.getSearchRevInclude(), IssueSeverity.WARNING); + compareSearchParams(sm, l.getSearchParam(), r.getSearchParam(), path, res, union.getSearchParam(), intersection.getSearchParam()); + compareOperations(sm, l.getOperation(), r.getOperation(), path, res, union.getOperation(), intersection.getOperation()); + } + + private void compareItemProperty(StructuralMatch combined, String name, PrimitiveType left, PrimitiveType right, String path, CapabilityStatementComparison res, PrimitiveType union, PrimitiveType intersection, IssueSeverity issueSeverity) { + if (!left.isEmpty() || !right.isEmpty()) { + if (left.isEmpty()) { + union.copyValues(right); + combined.getChildren().add(new StructuralMatch(vmI(issueSeverity, "Added this "+name, path), right).setName(name)); + } else if (right.isEmpty()) { + union.copyValues(left); + combined.getChildren().add(new StructuralMatch(left, vmI(issueSeverity, "Removed this expectation", path)).setName(name)); + } else { + StructuralMatch sm = new StructuralMatch(left, right).setName(name); + combined.getChildren().add(sm); + String ls = left.primitiveValue(); + String rs = right.primitiveValue(); + if (ls.equals(rs)) { + union.copyValues(left); + intersection.copyValues(left); + } else { + sm.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+ls+"' vs '"+rs+"'", issueSeverity)); + union.copyValues(left); + intersection.copyValues(left); + } + compareExpectations(sm, left, right, path, res, union, intersection); + } + } + } + + private void compareItemPropertyList(StructuralMatch combined, String name, List left, List right, String path, CapabilityStatementComparison res, List union, List intersection, IssueSeverity issueSeverity) { + List matchR = new ArrayList<>(); + for (T l : left) { + T r = findInListT(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(issueSeverity, "Removed this "+name, path)).setName(name)); + } else { + matchR.add(r); + union.add(l); + intersection.add(l); + StructuralMatch sm = new StructuralMatch(l, r).setName(name); + combined.getChildren().add(sm); + } + } + for (T r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vmI(issueSeverity, "Added this "+name, path), r).setName(name)); + } + } + } + + private T findInListT(List list, T item) { + for (T t : list) { + if (t.equalsDeep(item)) { + return t; + } + } + return null; + } + + + private CapabilityStatementRestResourceComponent mergeRestResource(CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r) { + CapabilityStatementRestResourceComponent res = l.copy(); + // todo: compare profiles, not just copy + if (!l.hasProfile() && r.hasProfile()) { + res.setProfile(r.getProfile()); + } + if (!l.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(r.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceComponent intersectRestResource(CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r) { + CapabilityStatementRestResourceComponent res = new CapabilityStatementRestResourceComponent(); + res.setType(l.getType()); + // todo: compare profiles, not just copy + if (l.hasProfile() && l.getProfile().equals(r.getProfile())) { + res.setProfile(l.getProfile()); + } + if (l.hasDocumentation() && l.getDocumentation().equals(r.getDocumentation())) { + res.setDocumentation(l.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceComponent findInList(List list, CapabilityStatementRestResourceComponent item) { + for (CapabilityStatementRestResourceComponent t : list) { + if (t.hasType() && t.getType().equals(item.getType())) { + return t; + } + } + return null; + } + + private void compareRestResourceInteractions(StructuralMatch combined, CapabilityStatementRestResourceComponent left, CapabilityStatementRestResourceComponent right, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) { + List matchR = new ArrayList<>(); + for (ResourceInteractionComponent l : left.getInteraction()) { + ResourceInteractionComponent r = findInList(right.getInteraction(), l); + if (r == null) { + union.getInteraction().add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); + } else { + matchR.add(r); + ResourceInteractionComponent cdM = mergeRestResourceInteractions(l, r); + ResourceInteractionComponent cdI = intersectRestResourceInteractions(l, r); + union.getInteraction().add(cdM); + intersection.getInteraction().add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); + compareExpectations(sm, l, r, path, res, union, intersection); + combined.getChildren().add(sm); + } + } + for (ResourceInteractionComponent r : right.getInteraction()) { + if (!matchR.contains(r)) { + union.getInteraction().add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); + } + } + } + + private ResourceInteractionComponent mergeRestResourceInteractions(ResourceInteractionComponent l, ResourceInteractionComponent r) { + ResourceInteractionComponent res = l.copy(); + if (!res.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(r.getDocumentation()); + } + return res; + } + + private ResourceInteractionComponent intersectRestResourceInteractions(ResourceInteractionComponent l, ResourceInteractionComponent r) { + ResourceInteractionComponent res = l.copy(); + if (res.hasDocumentation() && !r.hasDocumentation()) { + res.setDocumentation(null); + } + return res; + } + + private ResourceInteractionComponent findInList(List list, ResourceInteractionComponent item) { + for (ResourceInteractionComponent t : list) { + if (t.hasCode() && t.getCode().equals(item.getCode())) { + return t; + } + } + return null; + } + + + private void compareSearchParams(StructuralMatch combined, List left, List right, String path, CapabilityStatementComparison res, List union, List intersection) { + List matchR = new ArrayList<>(); + for (CapabilityStatementRestResourceSearchParamComponent l : left) { + CapabilityStatementRestResourceSearchParamComponent r = findInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this Search Parameter", path))); + } else { + matchR.add(r); + CapabilityStatementRestResourceSearchParamComponent cdM = mergeSearchParams(l, r); + CapabilityStatementRestResourceSearchParamComponent cdI = intersectSearchParams(l, r); + union.add(cdM); + intersection.add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); + compareItemProperty(sm, "type", l.getTypeElement(), r.getTypeElement(), path, res, cdM.getTypeElement(), cdI.getTypeElement(), IssueSeverity.ERROR); + compareItemProperty(sm, "definition", l.getDefinitionElement(), r.getDefinitionElement(), path, res, cdM.getDefinitionElement(), cdI.getDefinitionElement(), IssueSeverity.ERROR); + compareExpectations(sm, l, r, path, res, cdM, cdI); + combined.getChildren().add(sm); + } + } + for (CapabilityStatementRestResourceSearchParamComponent r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this Search Parameter", path), r)); + } + } + } + + private CapabilityStatementRestResourceSearchParamComponent mergeSearchParams(CapabilityStatementRestResourceSearchParamComponent l, CapabilityStatementRestResourceSearchParamComponent r) { + CapabilityStatementRestResourceSearchParamComponent res = l.copy(); + if (!res.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(r.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceSearchParamComponent intersectSearchParams(CapabilityStatementRestResourceSearchParamComponent l, CapabilityStatementRestResourceSearchParamComponent r) { + CapabilityStatementRestResourceSearchParamComponent res = new CapabilityStatementRestResourceSearchParamComponent(); + res.setName(l.getName()); + if (l.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(l.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceSearchParamComponent findInList(List list, CapabilityStatementRestResourceSearchParamComponent item) { + for (CapabilityStatementRestResourceSearchParamComponent t : list) { + if (t.hasName() && t.getName().equals(item.getName())) { + return t; + } + } + return null; + } + + + private void compareOperations(StructuralMatch combined, List left, List right, String path, CapabilityStatementComparison res, List union, List intersection) { + List matchR = new ArrayList<>(); + for (CapabilityStatementRestResourceOperationComponent l : left) { + CapabilityStatementRestResourceOperationComponent r = findInList(right, l); + if (r == null) { + union.add(l); + combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this Search Parameter", path))); + } else { + matchR.add(r); + CapabilityStatementRestResourceOperationComponent cdM = mergeOperations(l, r); + CapabilityStatementRestResourceOperationComponent cdI = intersectOperations(l, r); + union.add(cdM); + intersection.add(cdI); + StructuralMatch sm = new StructuralMatch(l, r); + compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); + compareItemProperty(sm, "definition", l.getDefinitionElement(), r.getDefinitionElement(), path, res, cdM.getDefinitionElement(), cdI.getDefinitionElement(), IssueSeverity.ERROR); + compareExpectations(sm, l, r, path, res, cdM, cdI); + combined.getChildren().add(sm); + } + } + for (CapabilityStatementRestResourceOperationComponent r : right) { + if (!matchR.contains(r)) { + union.add(r); + combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this Search Parameter", path), r)); + } + } + } + + private CapabilityStatementRestResourceOperationComponent mergeOperations(CapabilityStatementRestResourceOperationComponent l, CapabilityStatementRestResourceOperationComponent r) { + CapabilityStatementRestResourceOperationComponent res = l.copy(); + if (!res.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(r.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceOperationComponent intersectOperations(CapabilityStatementRestResourceOperationComponent l, CapabilityStatementRestResourceOperationComponent r) { + CapabilityStatementRestResourceOperationComponent res = new CapabilityStatementRestResourceOperationComponent(); + res.setName(l.getName()); + if (l.hasDocumentation() && r.hasDocumentation()) { + res.setDocumentation(l.getDocumentation()); + } + return res; + } + + private CapabilityStatementRestResourceOperationComponent findInList(List list, CapabilityStatementRestResourceOperationComponent item) { + for (CapabilityStatementRestResourceOperationComponent t : list) { + if (t.hasName() && t.getName().equals(item.getName())) { + return t; + } + } + return null; + } + + + // 6 columns: path | left value | left doco | right value | right doco | comments + public XhtmlNode renderStatements(CapabilityStatementComparison comparison, String id, String prefix) throws FHIRException, IOException { + HierarchicalTableGenerator gen = new HierarchicalTableGenerator(Utilities.path("[tmp]", "compare"), false); + TableModel model = gen.new TableModel(id, true); + model.setAlternating(true); + model.getTitles().add(gen.new Title(null, null, "Type", "The type of item", null, 100)); + model.getTitles().add(gen.new Title(null, null, "Left Value", "The left value for the item", null, 200, 1)); + model.getTitles().add(gen.new Title(null, null, "Left Doco", "The left documentation for the item", null, 200, 1)); + model.getTitles().add(gen.new Title(null, null, "Right Value", "The right value for the item", null, 200, 1)); + model.getTitles().add(gen.new Title(null, null, "Right Doco", "The right documentation for the item", null, 200, 1)); + model.getTitles().add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200)); + for (StructuralMatch t : comparison.getCombined().getChildren()) { + addRow(gen, model.getRows(), t, comparison); + } + return gen.generate(model, prefix, 0, null); + } + + private void addRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = null; + if (t.either() instanceof CapabilityStatementRestComponent) { + r = addRestRow(gen, rows, t, comparison); + } else if (t.either() instanceof CapabilityStatementRestSecurityComponent) { + r = addRestSecurityRow(gen, rows, t, comparison); + } else if (t.either() instanceof CapabilityStatementRestResourceComponent) { + r = addRestResourceRow(gen, rows, t, comparison); + } else if (t.either() instanceof ResourceInteractionComponent) { + r = addRestResourceInteractionRow(gen, rows, t, comparison); + } else if (t.either() instanceof CapabilityStatementRestResourceSearchParamComponent) { + r = addRestSearchParamRow(gen, rows, t, comparison); + } else if (t.either() instanceof CapabilityStatementRestResourceOperationComponent) { + r = addRestOperationRow(gen, rows, t, comparison); + } else if (t.either() instanceof CodeableConcept) { + r = addRestSecurityServiceRow(gen, rows, t, comparison); + } else if (t.either() instanceof Extension) { + r = addExtensionRow(gen, rows, t, comparison); + } else if (t.either() instanceof PrimitiveType) { + r = addPrimitiveTypeRow(gen, rows, t, comparison); + } else { + throw new Error("Not Done Yet: "+t.either().getClass().getName()); + } + for (StructuralMatch c : t.getChildren()) { + addRow(gen, r.getSubRows(), c, comparison); + } + } + + private Row addRestRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "mode", null, null)); + CapabilityStatementRestComponent left = t.hasLeft() ? (CapabilityStatementRestComponent) t.getLeft() : null; + CapabilityStatementRestComponent right = t.hasRight() ? (CapabilityStatementRestComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getMode().toCode() : "", null, null), left != null ? left.getMode().toCode() : null, right != null ? right.getMode().toCode() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getMode().toCode() : "", null, null), left != null ? left.getMode().toCode() : null, right != null ? right.getMode().toCode() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestSecurityRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "security", null, null)); + CapabilityStatementRestSecurityComponent left = t.hasLeft() ? (CapabilityStatementRestSecurityComponent) t.getLeft() : null; + CapabilityStatementRestSecurityComponent right = t.hasRight() ? (CapabilityStatementRestSecurityComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getCorsElement().primitiveValue() : "", null, null), left != null ? left.getCorsElement().primitiveValue() : null, right != null ? right.getCorsElement().primitiveValue() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDescription() : "", null, null), left != null ? left.getDescription() : null, right != null ? right.getDescription() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getCorsElement().primitiveValue() : "", null, null), left != null ? left.getCorsElement().primitiveValue() : null, right != null ? right.getCorsElement().primitiveValue() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDescription() : "", null, null), left != null ? left.getDescription() : null, right != null ? right.getDescription() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestResourceRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "resource", null, null)); + CapabilityStatementRestResourceComponent left = t.hasLeft() ? (CapabilityStatementRestResourceComponent) t.getLeft() : null; + CapabilityStatementRestResourceComponent right = t.hasRight() ? (CapabilityStatementRestResourceComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getType() : "", null, null), left != null ? left.getType() : null, right != null ? right.getType() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getType() : "", null, null), left != null ? left.getType() : null, right != null ? right.getType() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestSearchParamRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "searchParam", null, null)); + CapabilityStatementRestResourceSearchParamComponent left = t.hasLeft() ? (CapabilityStatementRestResourceSearchParamComponent) t.getLeft() : null; + CapabilityStatementRestResourceSearchParamComponent right = t.hasRight() ? (CapabilityStatementRestResourceSearchParamComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getName() : "", null, null), left != null ? left.getName() : null, right != null ? right.getName() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getName() : "", null, null), left != null ? left.getName() : null, right != null ? right.getName() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestOperationRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "operation", null, null)); + CapabilityStatementRestResourceOperationComponent left = t.hasLeft() ? (CapabilityStatementRestResourceOperationComponent) t.getLeft() : null; + CapabilityStatementRestResourceOperationComponent right = t.hasRight() ? (CapabilityStatementRestResourceOperationComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getName() : "", null, null), left != null ? left.getName() : null, right != null ? right.getName() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getName() : "", null, null), left != null ? left.getName() : null, right != null ? right.getName() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestSecurityServiceRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "service", null, null)); + CodeableConcept left = t.hasLeft() ? (CodeableConcept) t.getLeft() : null; + CodeableConcept right = t.hasRight() ? (CodeableConcept) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? gen(left) : "", null, null), left != null ? gen(left) : null, right != null ? gen(right) : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getText() : "", null, null), left != null ? left.getText() : null, right != null ? right.getText() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? gen(right) : "", null, null), left != null ? gen(left) : null, right != null ? gen(right) : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getText() : "", null, null), left != null ? left.getText() : null, right != null ? right.getText() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addRestResourceInteractionRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "interaction", null, null)); + ResourceInteractionComponent left = t.hasLeft() ? (ResourceInteractionComponent) t.getLeft() : null; + ResourceInteractionComponent right = t.hasRight() ? (ResourceInteractionComponent) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getCode().getDisplay() : "", null, null), left != null ? left.getCode().getDisplay() : null, right != null ? right.getCode().getDisplay() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getCode().getDisplay() : "", null, null), left != null ? left.getCode().getDisplay() : null, right != null ? right.getCode().getDisplay() : null, false)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getDocumentation() : "", null, null), left != null ? left.getDocumentation() : null, right != null ? right.getDocumentation() : null, true)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Row addExtensionRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, "expectation", null, null)); + Extension left = t.hasLeft() ? (Extension) t.getLeft() : null; + Extension right = t.hasRight() ? (Extension) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.getValue().primitiveValue() : "", null, null), left != null ? left.getValue().primitiveValue() : null, right != null ? right.getValue().primitiveValue() : null, true)); + r.getCells().add(gen.new Cell(null, null, "", null, null)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.getValue().primitiveValue() : "", null, null), left != null ? left.getValue().primitiveValue() : null, right != null ? right.getValue().primitiveValue() : null, false)); + r.getCells().add(gen.new Cell(null, null, "", null, null)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + @SuppressWarnings("rawtypes") + private Row addPrimitiveTypeRow(HierarchicalTableGenerator gen, List rows, StructuralMatch t, CapabilityStatementComparison comparison) { + Row r = gen.new Row(); + rows.add(r); + r.getCells().add(gen.new Cell(null, null, t.getName(), null, null)); + PrimitiveType left = t.hasLeft() ? (PrimitiveType) t.getLeft() : null; + PrimitiveType right = t.hasRight() ? (PrimitiveType) t.getRight() : null; + r.getCells().add(style(gen.new Cell(null, null, left != null ? left.primitiveValue() : "", null, null), left != null ? left.primitiveValue() : null, right != null ? right.primitiveValue() : null, true)); + r.getCells().add(gen.new Cell(null, null, "", null, null)); + r.getCells().add(style(gen.new Cell(null, null, right != null ? right.primitiveValue() : "", null, null), left != null ? left.primitiveValue() : null, right != null ? right.primitiveValue() : null, false)); + r.getCells().add(gen.new Cell(null, null, "", null, null)); + r.getCells().add(cellForMessages(gen, t.getMessages())); + return r; + } + + private Cell style(Cell cell, String left, String right, boolean isLeft) { + if (left != null && right != null) { + if (!left.equals(right)) { + cell.setStyle("background-color: "+COLOR_DIFFERENT); + } + } else if (left != null) { + if (!isLeft) { + cell.setStyle("background-color: "+COLOR_NO_CELL_RIGHT); + } + } else if (right != null) { + if (isLeft) { + cell.setStyle("background-color: "+COLOR_NO_CELL_LEFT); + } + } + return cell; + } + + @Override + protected String fhirType() { + return "CapabilityStatement"; + } + +} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java index 7577e0f9c..ecb17a159 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java @@ -246,9 +246,9 @@ public class ResourceComparer { for (ValidationMessage vm : csc.messages) { XhtmlNode tr = tbl.tr(); tr.style("background-color: "+colorForLevel(vm.getLevel())); - tr.td().tx(vm.getLocation()); - tr.td().tx(vm.getMessage()); tr.td().tx(vm.getLevel().getDisplay()); + tr.td().tx(vm.getLocation()); + tr.td().tx(vm.getMessage().replace("\"", "'")); } return div; } @@ -267,6 +267,12 @@ public class ResourceComparer { } } + protected ValidationMessage vm(IssueSeverity level, String message, String path, List genMessages) { + ValidationMessage vm = new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path, message, level == IssueSeverity.NULL ? IssueSeverity.INFORMATION : level); + genMessages.add(vm); + return vm; + } + private String colorForLevel(IssueSeverity level) { switch (level) { case ERROR: diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java index 13930d1ce..3b231d94e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java @@ -9,6 +9,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; public class StructuralMatch { + private String name; // this is used in some contexts to carry name when T is a pretty abstract type private T left; private T right; private List messages = new ArrayList<>(); @@ -101,7 +102,15 @@ public class StructuralMatch { for (StructuralMatch c : children) { c.countMessages(cnts); } + } + public String getName() { + return name; + } + + public StructuralMatch setName(String name) { + this.name = name; + return this; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index d8ed01548..bf9d6b521 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -896,13 +896,17 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } private ValidationResult validateOnServer(ValueSet vs, Parameters pin) throws FHIRException { - if (vs != null) + if (vs != null) { pin.addParameter().setName("valueSet").setResource(vs); - for (ParametersParameterComponent pp : pin.getParameter()) - if (pp.getName().equals("profile")) + } + for (ParametersParameterComponent pp : pin.getParameter()) { + if (pp.getName().equals("profile")) { throw new Error(formatMessage(I18nConstants.CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT)); - if (expParameters == null) + } + } + if (expParameters == null) { throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); + } pin.addParameter().setName("profile").setResource(expParameters); if (txLog != null) { txLog.clearLastId(); @@ -911,10 +915,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); } Parameters pOut; - if (vs == null) + if (vs == null) { pOut = txClient.validateCS(pin); - else + } else { pOut = txClient.validateVS(pin); + } return processValidationResult(pOut); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 6fe9cde60..b065504c5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -426,7 +426,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon if (types.length == 0 && loader != null) { types = loader.getTypes(); } - if (VersionUtilities.isR2Ver(pi.fhirVersion())) { + if (VersionUtilities.isR2Ver(pi.fhirVersion()) || !pi.canLazyLoad()) { // can't lazy load R2 because of valueset/codesystem implementation if (types.length == 0) { types = new String[] { "StructureDefinition", "ValueSet", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem" }; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CodeableConcept.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CodeableConcept.java index bb07bb05c..a2bf74035 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CodeableConcept.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CodeableConcept.java @@ -353,6 +353,52 @@ public class CodeableConcept extends DataType implements ICompositeType { } return null; } + + public static CodeableConcept merge(CodeableConcept l, CodeableConcept r) { + CodeableConcept res = new CodeableConcept(); + List handled = new ArrayList<>(); + for (Coding c : l.getCoding()) { + boolean done = false; + for (Coding t : r.getCoding()) { + if (t.matches(c)) { + handled.add(t); + res.getCoding().add(Coding.merge(c, t)); + done = true; + break; + } + } + if (!done) { + res.getCoding().add(c.copy()); + } + } + for (Coding c : r.getCoding()) { + if (!handled.contains(c)) { + res.getCoding().add(c); + } + } + if (l.hasText()) { + res.setText(l.getText()); + } else { + res.setText(r.getText()); + } + return res; + } + + public static CodeableConcept intersect(CodeableConcept l, CodeableConcept r) { + CodeableConcept res = new CodeableConcept(); + for (Coding c : l.getCoding()) { + for (Coding t : r.getCoding()) { + if (t.matches(c)) { + res.getCoding().add(Coding.intersect(c, t)); + break; + } + } + } + if (l.hasText() && r.hasText() && l.getText().equals(r.getText())) { + res.setText(l.getText()); + } + return res; + } // end addition diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java index 7f2974a0e..eb3ca2efa 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Coding.java @@ -519,6 +519,56 @@ public class Coding extends DataType implements IBaseCoding, ICompositeType, ICo public boolean matches(Coding other) { return other.hasCode() && this.hasCode() && other.hasSystem() && this.hasSystem() && this.getCode().equals(other.getCode()) && this.getSystem().equals(other.getSystem()) ; + } + + public static Coding merge(Coding l, Coding r) { + Coding res = new Coding(); + if (l.hasSystem()) { + res.setSystem(l.getSystem()); + } else { + res.setSystem(r.getSystem()); + } + if (l.hasVersion()) { + res.setVersion(l.getVersion()); + } else { + res.setVersion(r.getVersion()); + } + if (l.hasCode()) { + res.setCode(l.getCode()); + } else { + res.setCode(r.getCode()); + } + if (l.hasDisplay()) { + res.setDisplay(l.getDisplay()); + } else { + res.setDisplay(r.getDisplay()); + } + if (l.hasUserSelected()) { + res.setUserSelected(l.getUserSelected()); + } else { + res.setUserSelected(r.getUserSelected()); + } + return res; + } + + public static Coding intersect(Coding l, Coding r) { + Coding res = new Coding(); + if (l.hasSystem() && l.getSystem().equals(r.getSystem())) { + res.setSystem(l.getSystem()); + } + if (l.hasVersion() && l.getVersion().equals(r.getVersion())) { + res.setVersion(l.getVersion()); + } + if (l.hasCode() && l.getCode().equals(r.getCode())) { + res.setCode(l.getCode()); + } + if (l.hasDisplay() && l.getDisplay().equals(r.getDisplay())) { + res.setDisplay(l.getDisplay()); + } + if (l.hasUserSelected() && l.getUserSelected() == r.getUserSelected()) { + res.setUserSelected(l.getUserSelected()); + } + return res; } // end addition diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index be047566d..a51bb8a9e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -22,6 +22,7 @@ import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.HumanName; import org.hl7.fhir.r5.model.HumanName.NameUse; import org.hl7.fhir.r5.model.Identifier; @@ -45,6 +46,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; @@ -784,7 +786,11 @@ public class DataRenderer extends Renderer { if (s.getEvent().size() > 0) { CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); for (DateTimeType p : s.getEvent()) { - c.append(p.toHumanDisplay()); + if (p.hasValue()) { + c.append(p.toHumanDisplay()); + } else if (!renderExpression(c, p)) { + c.append("??"); + } } b.append("Events: "+ c.toString()); } @@ -829,6 +835,15 @@ public class DataRenderer extends Renderer { return b.toString(); } + private boolean renderExpression(CommaSeparatedStringBuilder c, PrimitiveType p) { + Extension exp = p.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/cqf-expression"); + if (exp == null) { + return false; + } + c.append(exp.getValueExpression().getExpression()); + return true; + } + private String displayEventCode(EventTiming when) { switch (when) { case C: return "at meals"; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java index 577321914..abbb2f916 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java @@ -177,9 +177,17 @@ public class PatientRenderer extends ResourceRenderer { StringBuilder b = new StringBuilder(); b.append(display(name)); b.append(" "); - b.append(gender); + if (dob == null) { + b.append("??"); + } else { + b.append(gender); + } b.append(" "); - b.append(display(dob)); + if (dob == null) { + b.append("DoB Unknown"); + } else { + b.append(display(dob)); + } if (id != null) { b.append(" ( "); b.append(display(id)); @@ -191,9 +199,17 @@ public class PatientRenderer extends ResourceRenderer { public void describe(XhtmlNode x, HumanName name, String gender, DateType dob, Identifier id) throws UnsupportedEncodingException, IOException { render(x.b(), name); x.tx(" "); - x.tx(gender); + if (dob == null) { + x.tx("??"); + } else { + x.tx(gender); + } x.tx(" "); - render(x, dob); + if (dob == null) { + x.tx("DoB Unknown"); + } else { + render(x, dob); + } if (id != null) { x.tx(" ( "); render(x, id); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index 76598aa2b..6fab3a623 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -536,14 +536,16 @@ public class NpmPackage { public List listIndexedResources(String... types) throws IOException { List res = new ArrayList(); for (NpmPackageFolder folder : folders.values()) { - for (JsonElement e : folder.index.getAsJsonArray("files")) { - JsonObject fi = e.getAsJsonObject(); - if (Utilities.existsInList(JSONUtil.str(fi, "resourceType"), types)) { - res.add(new PackageResourceInformation(folder.folder.getAbsolutePath(), fi)); + if (folder.index != null) { + for (JsonElement e : folder.index.getAsJsonArray("files")) { + JsonObject fi = e.getAsJsonObject(); + if (Utilities.existsInList(JSONUtil.str(fi, "resourceType"), types)) { + res.add(new PackageResourceInformation(folder.folder.getAbsolutePath(), fi)); + } } } } -// Collections.sort(res, new PackageResourceInformationSorter()); + // Collections.sort(res, new PackageResourceInformationSorter()); return res; } @@ -1077,6 +1079,15 @@ public class NpmPackage { } return false; } + + public boolean canLazyLoad() { + for (NpmPackageFolder folder : folders.values()) { + if (folder.folder == null) { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java index 7a7705d9b..b9b7bb05c 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java @@ -115,15 +115,30 @@ public class PackageHacker { } public static String fixPackageUrl(String webref) { + if (webref == null) { + return null; + } // workaround for past publishing problems - if (webref.equals("file://C:\\GitHub\\hl7.fhir.us.qicore#4.0.0\\output")) { - return "http://hl7.org/fhir/us/qicore/STU4"; - } - if (webref.equals("file://C:\\GitHub\\hl7.fhir.us.cqfmeasures#2.0.0\\output")) { - return "http://hl7.org/fhir/us/cqfmeasures/STU2"; - } - if (webref.equals("file://C:\\GitHub\\hl7.fhir.uv.ips#1.0.0\\output")) { - return "http://hl7.org/fhir/uv/ips/STU1"; + switch (webref) { + case "file://C:\\GitHub\\hl7.fhir.us.breast-radiology#0.2.0\\output": return "http://hl7.org/fhir/us/breast-radiology/2020May"; + case "file://C:\\GitHub\\hl7.fhir.us.bser#1.0.0\\output": return "http://hl7.org/fhir/us/bser/STU1"; + case "file://C:\\GitHub\\hl7.fhir.us.carin-bb#0.1.0\\output": return "http://hl7.org/fhir/us/carin-bb/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.carin-rtpbc#0.1.0\\output": return "http://hl7.org/fhir/us/carin-rtpbc/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.cqfmeasures#1.1.0\\output": return "http://hl7.org/fhir/us/cqfmeasures/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.cqfmeasures#2.0.0\\output": return "http://hl7.org/fhir/us/cqfmeasures/STU2"; + case "file://C:\\GitHub\\hl7.fhir.us.davinci-alerts#0.2.0\\output": return "http://hl7.org/fhir/us/davinci-alerts/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.davinci-atr#0.1.0\\output": return "http://hl7.org/fhir/us/davinci-atr/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.davinci-deqm#1.1.0\\output": return "http://hl7.org/fhir/us/davinci-deqm/2020Feb"; + case "file://C:\\GitHub\\hl7.fhir.us.davinci-deqm#1.0.0\\output": return "http://hl7.org/fhir/us/davinci-deqm/STU1"; + case "file://C:\\GitHub\\hl7.fhir.us.dme-orders#0.1.1\\output": return "http://hl7.org/fhir/us/dme-orders/2020May"; + case "file://C:\\GitHub\\hl7.fhir.us.ecr#1.0.0\\output": return "http://hl7.org/fhir/us/ecr/STU1"; + case "file://C:\\GitHub\\hl7.fhir.us.mcode#1.0.0\\output": return "http://hl7.org/fhir/us/mcode/STU1"; + case "file://C:\\GitHub\\hl7.fhir.us.odh#1.0.0\\output": return "http://hl7.org/fhir/us/odh/STU1"; + case "file://C:\\GitHub\\hl7.fhir.us.qicore#4.0.0\\output": return "http://hl7.org/fhir/us/qicore/STU4"; + case "file://C:\\GitHub\\hl7.fhir.uv.ips#1.0.0\\output": return "http://hl7.org/fhir/uv/ips/STU1"; + case "file://C:\\GitHub\\hl7.fhir.uv.mhealth-framework#0.1.0\\output": return "http://hl7.org/fhir/uv/mhealth-framework/2020May"; + case "file://C:\\GitHub\\hl7.fhir.uv.security-label-ds4p#0.1.0\\output": return "http://hl7.org/fhir/uv/security-label-ds4p/2020May"; + case "file://C:\\GitHub\\hl7.fhir.uv.shorthand#0.12.0\\output": return "http://hl7.org/fhir/uv/shorthand/2020May"; } return webref; } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java index 130195e89..332e58051 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java @@ -11,6 +11,8 @@ import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.comparison.CapabilityStatementComparer; +import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison; import org.hl7.fhir.r5.comparison.CodeSystemComparer; import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; import org.hl7.fhir.r5.comparison.ComparisonSession; @@ -24,6 +26,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.Resource; @@ -154,6 +157,18 @@ public class ComparisonTests { String xmle = new XhtmlComposer(true).compose(pc.renderErrors(csc)); String xml1 = new XhtmlComposer(true).compose(pc.renderMetadata(csc, "", "")); String xml2 = new XhtmlComposer(true).compose(pc.renderStructure(csc, "", "", "http://hl7.org/fhir")); +// String xml3 = new XhtmlComposer(true).compose(cs.renderExpansion(csc, "", "")); + TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Structure") + xml2 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html")); + checkOutcomes(csc.getMessages(), content); + } else if (left instanceof CapabilityStatement && right instanceof CapabilityStatement) { + CapabilityStatementComparer pc = new CapabilityStatementComparer(session); + CapabilityStatementComparison csc = pc.compare((CapabilityStatement) left, (CapabilityStatement) right); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-union.json")), csc.getUnion()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path("[tmp]", "comparison", name + "-intersection.json")), csc.getIntersection()); + + String xmle = new XhtmlComposer(true).compose(pc.renderErrors(csc)); + String xml1 = new XhtmlComposer(true).compose(pc.renderMetadata(csc, "", "")); + String xml2 = new XhtmlComposer(true).compose(pc.renderStatements(csc, "", "")); // String xml3 = new XhtmlComposer(true).compose(cs.renderExpansion(csc, "", "")); TextFile.stringToFile(HEADER + hd("Messages") + xmle + BREAK + hd("Metadata") + xml1 + BREAK + hd("Structure") + xml2 + FOOTER, Utilities.path("[tmp]", "comparison", name + ".html")); checkOutcomes(csc.getMessages(), content);