001package org.hl7.fhir.r5.comparison; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Date; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009 010import org.hl7.fhir.exceptions.DefinitionException; 011import org.hl7.fhir.exceptions.FHIRException; 012import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts; 013import org.hl7.fhir.r5.context.IWorkerContext; 014import org.hl7.fhir.r5.model.BackboneElement; 015import org.hl7.fhir.r5.model.BooleanType; 016import org.hl7.fhir.r5.model.CanonicalResource; 017import org.hl7.fhir.r5.model.CanonicalType; 018import org.hl7.fhir.r5.model.CapabilityStatement; 019import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; 020import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; 021import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; 022import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; 023import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent; 024import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent; 025import org.hl7.fhir.r5.model.CapabilityStatement.ResourceVersionPolicy; 026import org.hl7.fhir.r5.model.CodeType; 027import org.hl7.fhir.r5.model.CodeableConcept; 028import org.hl7.fhir.r5.model.Coding; 029import org.hl7.fhir.r5.model.DataType; 030import org.hl7.fhir.r5.model.Element; 031import org.hl7.fhir.r5.model.Enumeration; 032import org.hl7.fhir.r5.model.Extension; 033import org.hl7.fhir.r5.model.PrimitiveType; 034import org.hl7.fhir.r5.model.Resource; 035import org.hl7.fhir.r5.model.StructureDefinition; 036import org.hl7.fhir.utilities.Utilities; 037import org.hl7.fhir.utilities.validation.ValidationMessage; 038import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 039import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 040import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 041import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 042import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 043import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 044import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 045import org.hl7.fhir.utilities.xhtml.XhtmlNode; 046 047public class CapabilityStatementComparer extends CanonicalResourceComparer { 048 049 050 public class CapabilityStatementComparison extends CanonicalResourceComparison<CapabilityStatement> { 051 052 private StructuralMatch<Element> combined; 053 054 public CapabilityStatementComparison(CapabilityStatement left, CapabilityStatement right) { 055 super(left, right); 056 combined = new StructuralMatch<Element>(); // base 057 } 058 059 public StructuralMatch<Element> getCombined() { 060 return combined; 061 } 062 063 @Override 064 protected String abbreviation() { 065 return "cps"; 066 } 067 068 @Override 069 protected String summary() { 070 return "CapabilityStatement: "+left.present()+" vs "+right.present(); 071 } 072 073 @Override 074 protected String fhirType() { 075 return "CapabilityStatement"; 076 } 077 078 @Override 079 protected void countMessages(MessageCounts cnts) { 080 super.countMessages(cnts); 081 combined.countMessages(cnts); 082 } 083 } 084 085 public CapabilityStatementComparer(ComparisonSession session) { 086 super(session); 087 } 088 089 public CapabilityStatementComparison compare(CapabilityStatement left, CapabilityStatement right) { 090 if (left == null) 091 throw new DefinitionException("No CapabilityStatement provided (left)"); 092 if (right == null) 093 throw new DefinitionException("No CapabilityStatement provided (right)"); 094 095 096 CapabilityStatementComparison res = new CapabilityStatementComparison(left, right); 097 session.identify(res); 098 CapabilityStatement cs = new CapabilityStatement(); 099 res.setUnion(cs); 100 session.identify(cs); 101 cs.setName("Union"+left.getName()+"And"+right.getName()); 102 cs.setTitle("Union of "+left.getTitle()+" And "+right.getTitle()); 103 cs.setStatus(left.getStatus()); 104 cs.setDate(new Date()); 105 106 CapabilityStatement cs1 = new CapabilityStatement(); 107 res.setIntersection(cs1); 108 session.identify(cs1); 109 cs1.setName("Intersection"+left.getName()+"And"+right.getName()); 110 cs1.setTitle("Intersection of "+left.getTitle()+" And "+right.getTitle()); 111 cs1.setStatus(left.getStatus()); 112 cs1.setDate(new Date()); 113 114 compareMetadata(left, right, res.getMetadata(), res); 115 comparePrimitives("kind", left.getKindElement(), right.getKindElement(), res.getMetadata(), IssueSeverity.ERROR, res); 116 compareCanonicalList("instantiates", left.getInstantiates(), right.getInstantiates(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getInstantiates(), cs1.getInstantiates()); 117 compareCanonicalList("imports", left.getImports(), right.getImports(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getImports(), cs1.getImports()); 118 comparePrimitives("software.name", left.getSoftware().getNameElement(), right.getSoftware().getNameElement(), res.getMetadata(), IssueSeverity.ERROR, res); 119 comparePrimitives("software.version", left.getSoftware().getVersionElement(), right.getSoftware().getVersionElement(), res.getMetadata(), IssueSeverity.ERROR, res); 120 comparePrimitives("software.releaseDate", left.getSoftware().getReleaseDateElement(), right.getSoftware().getReleaseDateElement(), res.getMetadata(), IssueSeverity.ERROR, res); 121 comparePrimitives("implementation.description", left.getImplementation().getDescriptionElement(), right.getImplementation().getDescriptionElement(), res.getMetadata(), IssueSeverity.ERROR, res); 122 comparePrimitives("implementation.url", left.getImplementation().getUrlElement(), right.getImplementation().getUrlElement(), res.getMetadata(), IssueSeverity.ERROR, res); 123 comparePrimitives("fhirVersion", left.getFhirVersionElement(), right.getFhirVersionElement(), res.getMetadata(), IssueSeverity.ERROR, res); 124 compareCodeList("format", left.getFormat(), right.getFormat(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getFormat(), cs1.getFormat()); 125 compareCodeList("patchFormat", left.getPatchFormat(), right.getPatchFormat(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getPatchFormat(), cs1.getPatchFormat()); 126 compareCanonicalList("implementationGuide", left.getImplementationGuide(), right.getImplementationGuide(), res.getMetadata(), IssueSeverity.ERROR, res, cs.getImplementationGuide(), cs1.getImplementationGuide()); 127 128 129 compareRests(left.getRest(), right.getRest(), res.getCombined(), res.getUnion().getRest(), res.getIntersection().getRest(), res.getUnion(), res.getIntersection(), res, "CapabilityStatement.rest"); 130 return res; 131 } 132 133 private void compareRests(List<CapabilityStatementRestComponent> left, List<CapabilityStatementRestComponent> right, StructuralMatch<Element> combined, List<CapabilityStatementRestComponent> union, List<CapabilityStatementRestComponent> intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { 134 List<CapabilityStatementRestComponent> matchR = new ArrayList<>(); 135 for (CapabilityStatementRestComponent l : left) { 136 CapabilityStatementRestComponent r = findInList(right, l); 137 if (r == null) { 138 union.add(l); 139 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); 140 } else { 141 matchR.add(r); 142 CapabilityStatementRestComponent cdM = merge(l, r, res); 143 CapabilityStatementRestComponent cdI = intersect(l, r, res); 144 union.add(cdM); 145 intersection.add(cdI); 146 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 147 compare(sm, l, r, path+".where(mode='"+l.getMode()+"')", res); 148 combined.getChildren().add(sm); 149 compareRestSecurity(l, r, sm, cdM.getSecurity(), cdI.getSecurity(), csU, csI, res, path+".security"); 150 compareRestResources(l, r, sm, cdM, cdI, csU, csI, res, path+".resource"); 151 compareSearchParams(combined, l.getSearchParam(), r.getSearchParam(), path, res, cdM.getSearchParam(), cdI.getSearchParam()); 152 compareOperations(combined, l.getOperation(), r.getOperation(), path, res, cdM.getOperation(), cdI.getOperation()); 153 compareItemPropertyList(sm, "compartment", l.getCompartment(), r.getCompartment(), path, res, cdM.getCompartment(), cdI.getCompartment(), IssueSeverity.ERROR); 154 } 155 } 156 for (CapabilityStatementRestComponent r : right) { 157 if (!matchR.contains(r)) { 158 union.add(r); 159 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); 160 } 161 } 162 } 163 164 private CapabilityStatementRestComponent findInList(List<CapabilityStatementRestComponent> list, CapabilityStatementRestComponent item) { 165 for (CapabilityStatementRestComponent t : list) { 166 if (t.getMode().equals(item.getMode())) { 167 return t; 168 } 169 } 170 return null; 171 } 172 173 private void compare(StructuralMatch<Element> sm, CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, String path, CapabilityStatementComparison res) { 174 compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.WARNING, res); 175 } 176 177 private void compareRestSecurity(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, StructuralMatch<Element> smp, CapabilityStatementRestSecurityComponent merge, CapabilityStatementRestSecurityComponent intersect, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { 178 CapabilityStatementRestSecurityComponent ls = l.hasSecurity() ? l.getSecurity() : null; 179 CapabilityStatementRestSecurityComponent rs = r.hasSecurity() ? r.getSecurity() : null; 180 181 StructuralMatch<Element> sm = new StructuralMatch<Element>(ls, rs); 182 smp.getChildren().add(sm); 183 compareBooleans(path, sm.getMessages(), l.getSecurity().getCorsElement(), r.getSecurity().getCorsElement(), "security.cors", IssueSeverity.WARNING, res); 184 compareStrings(path, sm.getMessages(), l.getSecurity().getDescription(), r.getSecurity().getDescription(), "security.description", IssueSeverity.INFORMATION, res); 185 compareRestSecurityService(ls, rs, sm, merge, intersect, csU, csI, res, path+".security"); 186 } 187 188 private void compareRestSecurityService(CapabilityStatementRestSecurityComponent left, CapabilityStatementRestSecurityComponent right, StructuralMatch<Element> combined, CapabilityStatementRestSecurityComponent union, CapabilityStatementRestSecurityComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { 189 List<CodeableConcept> matchR = new ArrayList<>(); 190 for (CodeableConcept l : left.getService()) { 191 CodeableConcept r = findInList(right.getService(), l); 192 if (r == null) { 193 union.getService().add(l); 194 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); 195 } else { 196 matchR.add(r); 197 CodeableConcept cdM = CodeableConcept.merge(l, r); 198 CodeableConcept cdI = CodeableConcept.intersect(l, r); 199 union.getService().add(cdM); 200 intersection.getService().add(cdI); 201 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 202 compare(sm, l, r, path, res); 203 combined.getChildren().add(sm); 204 } 205 } 206 for (CodeableConcept r : right.getService()) { 207 if (!matchR.contains(r)) { 208 union.getService().add(r); 209 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); 210 } 211 } 212 } 213 214 215 private void compare(StructuralMatch<Element> sm, CodeableConcept l, CodeableConcept r, String path, CapabilityStatementComparison res) { 216 compareStrings(path, sm.getMessages(), l.getText(), r.getText(), "text", IssueSeverity.INFORMATION, res); 217 List<Coding> matches = new ArrayList<>(); 218 for (Coding lc : l.getCoding()) { 219 boolean m = false; 220 for (Coding rc : r.getCoding()) { 221 if (lc.matches(rc)) { 222 matches.add(rc); 223 m = true; 224 } 225 } 226 if (!m) { 227 sm.getMessages().add(vmI(IssueSeverity.INFORMATION, "Value for "+gen(lc)+" removed", path)); 228 } 229 } 230 for (Coding rc : r.getCoding()) { 231 if (!matches.contains(rc)) { 232 sm.getMessages().add(vmI(IssueSeverity.INFORMATION, "Value for "+gen(rc)+" added", path)); 233 } 234 } 235 } 236 237 private CodeableConcept findInList(List<CodeableConcept> list, CodeableConcept item) { 238 for (CodeableConcept t : list) { 239 if (t.matches(item)) { 240 return t; 241 } 242 } 243 return null; 244 } 245 246 private void compareStrings(String path, List<ValidationMessage> msgs, String left, String right, String name, IssueSeverity level, CapabilityStatementComparison res) { 247 if (!Utilities.noString(right)) { 248 if (Utilities.noString(left)) { 249 msgs.add(vmI(level, "Value for "+name+" added", path)); 250 } else if (!left.equals(right)) { 251 if (level != IssueSeverity.NULL) { 252 res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level)); 253 } 254 msgs.add(vmI(level, name+" changed from left to right", path)); 255 } 256 } else if (!Utilities.noString(left)) { 257 msgs.add(vmI(level, "Value for "+name+" removed", path)); 258 } 259 } 260 261 private void compareExpectations(StructuralMatch<Element> combined, Element left, Element right, String path, CapabilityStatementComparison res, Element union, Element intersection) { 262 Extension l = left.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation"); 263 Extension r = right.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation"); 264 if (l != null || r != null) { 265 if (l == null) { 266 union.addExtension(r.copy()); 267 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this expectation", path), r)); 268 } else if (r == null) { 269 union.addExtension(l.copy()); 270 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this expectation", path))); 271 } else { 272 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 273 combined.getChildren().add(sm); 274 String ls = l.getValue().primitiveValue(); 275 String rs = r.getValue().primitiveValue(); 276 if (ls.equals(rs)) { 277 union.addExtension(l.copy()); 278 intersection.addExtension(l.copy()); 279 } else { 280 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)); 281 String lowest = lower(ls, rs) ? ls : rs; 282 String highest = lower(ls, rs) ? rs : ls; 283 union.addExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", new CodeType(lowest)); 284 intersection.addExtension("http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", new CodeType(highest)); 285 } 286 } 287 } 288 } 289 290 private boolean lower(String ls, String rs) { 291 if (ls.equals("MAY")) { 292 return true; 293 } 294 if (ls.equals("SHALL")) { 295 return false; 296 } 297 if (rs.equals("MAY")) { 298 return false; 299 } 300 if (rs.equals("SHALL")) { 301 return true; 302 } 303 return false; 304 } 305 306 private void compareBooleans(String path, List<ValidationMessage> msgs, BooleanType left, BooleanType right, String name, IssueSeverity level, CapabilityStatementComparison res) { 307 if (!right.isEmpty()) { 308 if (left.isEmpty()) { 309 msgs.add(vmI(level, "Value for "+name+" added", path)); 310 } else if (left.getValue() != right.getValue()) { 311 if (level != IssueSeverity.NULL) { 312 res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level)); 313 } 314 msgs.add(vmI(level, name+" changed from left to right", path)); 315 } 316 } else if (!left.isEmpty()) { 317 msgs.add(vmI(level, "Value for "+name+" removed", path)); 318 } 319 } 320 321 private CapabilityStatementRestComponent merge(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, CapabilityStatementComparison res) { 322 CapabilityStatementRestComponent cd = l.copy(); 323 if (!l.hasDocumentation() && r.hasDocumentation()) { 324 cd.setDocumentation(r.getDocumentation()); 325 } 326 if (r.hasSecurity()) { 327 if (!l.getSecurity().hasCors() && r.getSecurity().hasCors()) { 328 cd.getSecurity().setCors(r.getSecurity().getCors()); 329 } 330 mergeCodeableConcepts(cd.getSecurity().getService(), r.getSecurity().getService()); 331 if (!l.getSecurity().hasDescription() && r.getSecurity().hasDescription()) { 332 cd.getSecurity().setDescription(r.getSecurity().getDescription()); 333 } 334 } 335 return cd; 336 } 337 338 private void mergeCodeableConcepts(List<CodeableConcept> tgt, List<CodeableConcept> src) { 339 for (CodeableConcept cd : src) { 340 boolean add = true; 341 for (CodeableConcept t : tgt) { 342 if (t.matches(cd)) { 343 add = false; 344 } 345 } 346 if (add) { 347 tgt.add(cd.copy()); 348 } 349 } 350 } 351 352 private CapabilityStatementRestComponent intersect(CapabilityStatementRestComponent l, CapabilityStatementRestComponent r, CapabilityStatementComparison res) { 353 CapabilityStatementRestComponent cd = l.copy(); 354 if (l.hasDocumentation() && !r.hasDocumentation()) { 355 cd.setDocumentation(null); 356 } 357 if (!r.hasSecurity()) { 358 cd.setSecurity(null); 359 } else { 360 if (!r.getSecurity().hasCors()) { 361 cd.getSecurity().setCorsElement(null); 362 } 363 intersectCodeableConcepts(cd.getSecurity().getService(), r.getSecurity().getService()); 364 if (!r.getSecurity().hasDescription()) { 365 cd.getSecurity().setDescription(null); 366 } 367 } 368 return cd; 369 } 370 371 private void intersectCodeableConcepts(List<CodeableConcept> tgt, List<CodeableConcept> src) { 372 List<CodeableConcept> toRemove = new ArrayList<CodeableConcept>(); 373 for (CodeableConcept cd : src) { 374 boolean remove = false; 375 for (CodeableConcept t : tgt) { 376 if (t.matches(cd)) { 377 remove = true; 378 } 379 } 380 if (remove) { 381 toRemove.add(cd); 382 } 383 } 384 tgt.removeAll(toRemove); 385 } 386 387 private void compareRestResources(CapabilityStatementRestComponent left, CapabilityStatementRestComponent right, StructuralMatch<Element> combined, CapabilityStatementRestComponent union, CapabilityStatementRestComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) { 388 List<CapabilityStatementRestResourceComponent> matchR = new ArrayList<>(); 389 for (CapabilityStatementRestResourceComponent l : left.getResource()) { 390 CapabilityStatementRestResourceComponent r = findInList(right.getResource(), l); 391 if (r == null) { 392 union.getResource().add(l); 393 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); 394 } else { 395 matchR.add(r); 396 CapabilityStatementRestResourceComponent cdM = mergeRestResource(l, r); 397 CapabilityStatementRestResourceComponent cdI = intersectRestResource(l, r); 398 union.getResource().add(cdM); 399 intersection.getResource().add(cdI); 400 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 401 compareRestResource(sm, l, r, path, res, cdM, cdI); 402 combined.getChildren().add(sm); 403 } 404 } 405 for (CapabilityStatementRestResourceComponent r : right.getResource()) { 406 if (!matchR.contains(r)) { 407 union.getResource().add(r); 408 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); 409 } 410 } 411 } 412 413 private void compareRestResource(StructuralMatch<Element> sm, CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) { 414 compareProfiles(path, sm, l.getProfileElement(), r.getProfileElement(), res, union, intersection); 415 // todo: supported profiles 416 compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); 417 compareExpectations(sm, l, r, path, res, union, intersection); 418 compareRestResourceInteractions(sm, l, r, path, res, union, intersection); 419 compareItemProperty(sm, "versioning", l.getVersioningElement(), r.getVersioningElement(), path, res, union.getVersioningElement(), intersection.getVersioningElement(), IssueSeverity.WARNING); 420 compareItemProperty(sm, "readHistory", l.getReadHistoryElement(), r.getReadHistoryElement(), path, res, union.getReadHistoryElement(), intersection.getReadHistoryElement(), IssueSeverity.INFORMATION); 421 compareItemProperty(sm, "updateCreate", l.getUpdateCreateElement(), r.getUpdateCreateElement(), path, res, union.getUpdateCreateElement(), intersection.getUpdateCreateElement(), IssueSeverity.WARNING); 422 compareItemProperty(sm, "conditionalCreate", l.getConditionalCreateElement(), r.getConditionalCreateElement(), path, res, union.getConditionalCreateElement(), intersection.getConditionalCreateElement(), IssueSeverity.WARNING); 423 compareItemProperty(sm, "conditionalRead", l.getConditionalReadElement(), r.getConditionalReadElement(), path, res, union.getConditionalReadElement(), intersection.getConditionalReadElement(), IssueSeverity.WARNING); 424 compareItemProperty(sm, "conditionalUpdate", l.getConditionalUpdateElement(), r.getConditionalUpdateElement(), path, res, union.getConditionalUpdateElement(), intersection.getConditionalUpdateElement(), IssueSeverity.WARNING); 425 compareItemProperty(sm, "conditionalDelete", l.getConditionalDeleteElement(), r.getConditionalDeleteElement(), path, res, union.getConditionalDeleteElement(), intersection.getConditionalDeleteElement(), IssueSeverity.WARNING); 426 compareItemPropertyList(sm, "referencePolicy", l.getReferencePolicy(), r.getReferencePolicy(), path, res, union.getReferencePolicy(), intersection.getReferencePolicy(), IssueSeverity.WARNING); 427 compareItemPropertyList(sm, "searchInclude", l.getSearchInclude(), r.getSearchInclude(), path, res, union.getSearchInclude(), intersection.getSearchInclude(), IssueSeverity.WARNING); 428 compareItemPropertyList(sm, "searchRevInclude", l.getSearchRevInclude(), r.getSearchRevInclude(), path, res, union.getSearchRevInclude(), intersection.getSearchRevInclude(), IssueSeverity.WARNING); 429 compareSearchParams(sm, l.getSearchParam(), r.getSearchParam(), path, res, union.getSearchParam(), intersection.getSearchParam()); 430 compareOperations(sm, l.getOperation(), r.getOperation(), path, res, union.getOperation(), intersection.getOperation()); 431 } 432 433 private void compareProfiles(String path, StructuralMatch<Element> combined, CanonicalType left, CanonicalType right, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) { 434 if (!left.hasValue() && !right.hasValue()) { 435 // nothing in this case 436 } else if (!left.hasValue()) { 437 // the intersection is anything in right. The union is everything (or nothing, in this case) 438 intersection.setProfileElement(right.copy()); 439 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.WARNING, "Added this profile", path), right).setName("profile")); 440 } else if (!right.hasValue()) { 441 // the intersection is anything in right. The union is everything (or nothing, in this case) 442 intersection.setProfileElement(left.copy()); 443 combined.getChildren().add(new StructuralMatch<Element>(left, vmI(IssueSeverity.WARNING, "Removed this profile", path)).setName("profile")); 444 } else { 445 // profiles on both sides... 446 StructureDefinition sdLeft = session.getContextLeft().fetchResource(StructureDefinition.class, left.getValue()); 447 StructureDefinition sdRight = session.getContextRight().fetchResource(StructureDefinition.class, right.getValue()); 448 if (sdLeft == null && sdRight == null) { 449 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.ERROR, "Cannot compare profiles because neither is known", path)).setName("profile")); 450 } else if (sdLeft == null) { 451 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.ERROR, "Cannot compare profiles because '"+left.getValue()+"' is not known", path)).setName("profile")); 452 } else if (sdRight == null) { 453 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.ERROR, "Cannot compare profiles because '"+right.getValue()+"' is not known", path)).setName("profile")); 454 } else if (sdLeft.getUrl().equals(sdRight.getUrl())) { 455 intersection.setProfileElement(left.copy()); 456 union.setProfileElement(left.copy()); 457 combined.getChildren().add(new StructuralMatch<Element>(left, right).setName("profile")); 458 } else if (profileInherits(sdLeft, sdRight, session.getContextLeft())) { 459 // if left inherits from right: 460 intersection.setProfileElement(left.copy()); 461 union.setProfileElement(right.copy()); 462 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.WARNING, "Changed this profile to a broader profile", path)).setName("profile")); 463 } else if (profileInherits(sdRight, sdLeft, session.getContextRight())) { 464 intersection.setProfileElement(right.copy()); 465 union.setProfileElement(left.copy()); 466 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.WARNING, "Changed this profile to a narrower one", path)).setName("profile")); 467 } else { 468 combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.WARNING, "Different", path)).setName("profile")); 469 throw new Error("Not done yet"); 470 } 471 } 472 } 473 474 private boolean profileInherits(StructureDefinition sdFocus, StructureDefinition sdOther, IWorkerContext ctxt) { 475 while (sdFocus != null) { 476 if (sdFocus.getUrl().equals(sdOther.getUrl()) && sdFocus.getVersion().equals(sdOther.getVersion())) { 477 return true; 478 } 479 sdFocus = ctxt.fetchResource(StructureDefinition.class, sdFocus.getBaseDefinition()); 480 } 481 return false; 482 } 483 484 private <T> void compareItemProperty(StructuralMatch<Element> combined, String name, PrimitiveType<T> left, PrimitiveType<T> right, String path, CapabilityStatementComparison res, PrimitiveType<T> union, PrimitiveType<T> intersection, IssueSeverity issueSeverity) { 485 if (!left.isEmpty() || !right.isEmpty()) { 486 if (left.isEmpty()) { 487 union.copyValues(right); 488 combined.getChildren().add(new StructuralMatch<Element>(vmI(issueSeverity, "Added this "+name, path), right).setName(name)); 489 } else if (right.isEmpty()) { 490 union.copyValues(left); 491 combined.getChildren().add(new StructuralMatch<Element>(left, vmI(issueSeverity, "Removed this expectation", path)).setName(name)); 492 } else { 493 StructuralMatch<Element> sm = new StructuralMatch<Element>(left, right).setName(name); 494 combined.getChildren().add(sm); 495 String ls = left.primitiveValue(); 496 String rs = right.primitiveValue(); 497 if (ls.equals(rs)) { 498 union.copyValues(left); 499 intersection.copyValues(left); 500 } else { 501 sm.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+ls+"' vs '"+rs+"'", issueSeverity)); 502 union.copyValues(left); 503 intersection.copyValues(left); 504 } 505 compareExpectations(sm, left, right, path, res, union, intersection); 506 } 507 } 508 } 509 510 private <T extends Element> void compareItemPropertyList(StructuralMatch<Element> combined, String name, List<T> left, List<T> right, String path, CapabilityStatementComparison res, List<T> union, List<T> intersection, IssueSeverity issueSeverity) { 511 List<T> matchR = new ArrayList<>(); 512 for (T l : left) { 513 T r = findInListT(right, l); 514 if (r == null) { 515 union.add(l); 516 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(issueSeverity, "Removed this "+name, path)).setName(name)); 517 } else { 518 matchR.add(r); 519 union.add(l); 520 intersection.add(l); 521 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r).setName(name); 522 combined.getChildren().add(sm); 523 } 524 } 525 for (T r : right) { 526 if (!matchR.contains(r)) { 527 union.add(r); 528 combined.getChildren().add(new StructuralMatch<Element>(vmI(issueSeverity, "Added this "+name, path), r).setName(name)); 529 } 530 } 531 } 532 533 private <T extends Element> T findInListT(List<T> list, T item) { 534 for (T t : list) { 535 if (t.equalsDeep(item)) { 536 return t; 537 } 538 } 539 return null; 540 } 541 542 543 private CapabilityStatementRestResourceComponent mergeRestResource(CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r) { 544 CapabilityStatementRestResourceComponent res = l.copy(); 545 // todo: compare profiles, not just copy 546 if (!l.hasProfile() && r.hasProfile()) { 547 res.setProfile(r.getProfile()); 548 } 549 if (!l.hasDocumentation() && r.hasDocumentation()) { 550 res.setDocumentation(r.getDocumentation()); 551 } 552 return res; 553 } 554 555 private CapabilityStatementRestResourceComponent intersectRestResource(CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r) { 556 CapabilityStatementRestResourceComponent res = new CapabilityStatementRestResourceComponent(); 557 res.setType(l.getType()); 558 // todo: compare profiles, not just copy 559 if (l.hasProfile() && l.getProfile().equals(r.getProfile())) { 560 res.setProfile(l.getProfile()); 561 } 562 if (l.hasDocumentation() && l.getDocumentation().equals(r.getDocumentation())) { 563 res.setDocumentation(l.getDocumentation()); 564 } 565 return res; 566 } 567 568 private CapabilityStatementRestResourceComponent findInList(List<CapabilityStatementRestResourceComponent> list, CapabilityStatementRestResourceComponent item) { 569 for (CapabilityStatementRestResourceComponent t : list) { 570 if (t.hasType() && t.getType().equals(item.getType())) { 571 return t; 572 } 573 } 574 return null; 575 } 576 577 private void compareRestResourceInteractions(StructuralMatch<Element> combined, CapabilityStatementRestResourceComponent left, CapabilityStatementRestResourceComponent right, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) { 578 List<ResourceInteractionComponent> matchR = new ArrayList<>(); 579 for (ResourceInteractionComponent l : left.getInteraction()) { 580 ResourceInteractionComponent r = findInList(right.getInteraction(), l); 581 if (r == null) { 582 union.getInteraction().add(l); 583 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this item", path))); 584 } else { 585 matchR.add(r); 586 ResourceInteractionComponent cdM = mergeRestResourceInteractions(l, r); 587 ResourceInteractionComponent cdI = intersectRestResourceInteractions(l, r); 588 union.getInteraction().add(cdM); 589 intersection.getInteraction().add(cdI); 590 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 591 compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); 592 compareExpectations(sm, l, r, path, res, union, intersection); 593 combined.getChildren().add(sm); 594 } 595 } 596 for (ResourceInteractionComponent r : right.getInteraction()) { 597 if (!matchR.contains(r)) { 598 union.getInteraction().add(r); 599 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); 600 } 601 } 602 } 603 604 private ResourceInteractionComponent mergeRestResourceInteractions(ResourceInteractionComponent l, ResourceInteractionComponent r) { 605 ResourceInteractionComponent res = l.copy(); 606 if (!res.hasDocumentation() && r.hasDocumentation()) { 607 res.setDocumentation(r.getDocumentation()); 608 } 609 return res; 610 } 611 612 private ResourceInteractionComponent intersectRestResourceInteractions(ResourceInteractionComponent l, ResourceInteractionComponent r) { 613 ResourceInteractionComponent res = l.copy(); 614 if (res.hasDocumentation() && !r.hasDocumentation()) { 615 res.setDocumentation(null); 616 } 617 return res; 618 } 619 620 private ResourceInteractionComponent findInList(List<ResourceInteractionComponent> list, ResourceInteractionComponent item) { 621 for (ResourceInteractionComponent t : list) { 622 if (t.hasCode() && t.getCode().equals(item.getCode())) { 623 return t; 624 } 625 } 626 return null; 627 } 628 629 630 private void compareSearchParams(StructuralMatch<Element> combined, List<CapabilityStatementRestResourceSearchParamComponent> left, List<CapabilityStatementRestResourceSearchParamComponent> right, String path, CapabilityStatementComparison res, List<CapabilityStatementRestResourceSearchParamComponent> union, List<CapabilityStatementRestResourceSearchParamComponent> intersection) { 631 List<CapabilityStatementRestResourceSearchParamComponent> matchR = new ArrayList<>(); 632 for (CapabilityStatementRestResourceSearchParamComponent l : left) { 633 CapabilityStatementRestResourceSearchParamComponent r = findInList(right, l); 634 if (r == null) { 635 union.add(l); 636 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this Search Parameter", path))); 637 } else { 638 matchR.add(r); 639 CapabilityStatementRestResourceSearchParamComponent cdM = mergeSearchParams(l, r); 640 CapabilityStatementRestResourceSearchParamComponent cdI = intersectSearchParams(l, r); 641 union.add(cdM); 642 intersection.add(cdI); 643 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 644 compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); 645 compareItemProperty(sm, "type", l.getTypeElement(), r.getTypeElement(), path, res, cdM.getTypeElement(), cdI.getTypeElement(), IssueSeverity.ERROR); 646 compareItemProperty(sm, "definition", l.getDefinitionElement(), r.getDefinitionElement(), path, res, cdM.getDefinitionElement(), cdI.getDefinitionElement(), IssueSeverity.ERROR); 647 compareExpectations(sm, l, r, path, res, cdM, cdI); 648 combined.getChildren().add(sm); 649 } 650 } 651 for (CapabilityStatementRestResourceSearchParamComponent r : right) { 652 if (!matchR.contains(r)) { 653 union.add(r); 654 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this Search Parameter", path), r)); 655 } 656 } 657 } 658 659 private CapabilityStatementRestResourceSearchParamComponent mergeSearchParams(CapabilityStatementRestResourceSearchParamComponent l, CapabilityStatementRestResourceSearchParamComponent r) { 660 CapabilityStatementRestResourceSearchParamComponent res = l.copy(); 661 if (!res.hasDocumentation() && r.hasDocumentation()) { 662 res.setDocumentation(r.getDocumentation()); 663 } 664 return res; 665 } 666 667 private CapabilityStatementRestResourceSearchParamComponent intersectSearchParams(CapabilityStatementRestResourceSearchParamComponent l, CapabilityStatementRestResourceSearchParamComponent r) { 668 CapabilityStatementRestResourceSearchParamComponent res = new CapabilityStatementRestResourceSearchParamComponent(); 669 res.setName(l.getName()); 670 if (l.hasDocumentation() && r.hasDocumentation()) { 671 res.setDocumentation(l.getDocumentation()); 672 } 673 return res; 674 } 675 676 private CapabilityStatementRestResourceSearchParamComponent findInList(List<CapabilityStatementRestResourceSearchParamComponent> list, CapabilityStatementRestResourceSearchParamComponent item) { 677 for (CapabilityStatementRestResourceSearchParamComponent t : list) { 678 if (t.hasName() && t.getName().equals(item.getName())) { 679 return t; 680 } 681 } 682 return null; 683 } 684 685 686 private void compareOperations(StructuralMatch<Element> combined, List<CapabilityStatementRestResourceOperationComponent> left, List<CapabilityStatementRestResourceOperationComponent> right, String path, CapabilityStatementComparison res, List<CapabilityStatementRestResourceOperationComponent> union, List<CapabilityStatementRestResourceOperationComponent> intersection) { 687 List<CapabilityStatementRestResourceOperationComponent> matchR = new ArrayList<>(); 688 for (CapabilityStatementRestResourceOperationComponent l : left) { 689 CapabilityStatementRestResourceOperationComponent r = findInList(right, l); 690 if (r == null) { 691 union.add(l); 692 combined.getChildren().add(new StructuralMatch<Element>(l, vmI(IssueSeverity.INFORMATION, "Removed this Search Parameter", path))); 693 } else { 694 matchR.add(r); 695 CapabilityStatementRestResourceOperationComponent cdM = mergeOperations(l, r); 696 CapabilityStatementRestResourceOperationComponent cdI = intersectOperations(l, r); 697 union.add(cdM); 698 intersection.add(cdI); 699 StructuralMatch<Element> sm = new StructuralMatch<Element>(l, r); 700 compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res); 701 compareItemProperty(sm, "definition", l.getDefinitionElement(), r.getDefinitionElement(), path, res, cdM.getDefinitionElement(), cdI.getDefinitionElement(), IssueSeverity.ERROR); 702 compareExpectations(sm, l, r, path, res, cdM, cdI); 703 combined.getChildren().add(sm); 704 } 705 } 706 for (CapabilityStatementRestResourceOperationComponent r : right) { 707 if (!matchR.contains(r)) { 708 union.add(r); 709 combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this Search Parameter", path), r)); 710 } 711 } 712 } 713 714 private CapabilityStatementRestResourceOperationComponent mergeOperations(CapabilityStatementRestResourceOperationComponent l, CapabilityStatementRestResourceOperationComponent r) { 715 CapabilityStatementRestResourceOperationComponent res = l.copy(); 716 if (!res.hasDocumentation() && r.hasDocumentation()) { 717 res.setDocumentation(r.getDocumentation()); 718 } 719 return res; 720 } 721 722 private CapabilityStatementRestResourceOperationComponent intersectOperations(CapabilityStatementRestResourceOperationComponent l, CapabilityStatementRestResourceOperationComponent r) { 723 CapabilityStatementRestResourceOperationComponent res = new CapabilityStatementRestResourceOperationComponent(); 724 res.setName(l.getName()); 725 if (l.hasDocumentation() && r.hasDocumentation()) { 726 res.setDocumentation(l.getDocumentation()); 727 } 728 return res; 729 } 730 731 private CapabilityStatementRestResourceOperationComponent findInList(List<CapabilityStatementRestResourceOperationComponent> list, CapabilityStatementRestResourceOperationComponent item) { 732 for (CapabilityStatementRestResourceOperationComponent t : list) { 733 if (t.hasName() && t.getName().equals(item.getName())) { 734 return t; 735 } 736 } 737 return null; 738 } 739 740 741 // 6 columns: path | left value | left doco | right value | right doco | comments 742 public XhtmlNode renderStatements(CapabilityStatementComparison comparison, String id, String prefix) throws FHIRException, IOException { 743 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(Utilities.path("[tmp]", "compare"), false); 744 TableModel model = gen.new TableModel(id, true); 745 model.setAlternating(true); 746 model.getTitles().add(gen.new Title(null, null, "Type", "The type of item", null, 100)); 747 model.getTitles().add(gen.new Title(null, null, "Left Value", "The left value for the item", null, 200, 1)); 748 model.getTitles().add(gen.new Title(null, null, "Left Doco", "The left documentation for the item", null, 200, 1)); 749 model.getTitles().add(gen.new Title(null, null, "Right Value", "The right value for the item", null, 200, 1)); 750 model.getTitles().add(gen.new Title(null, null, "Right Doco", "The right documentation for the item", null, 200, 1)); 751 model.getTitles().add(gen.new Title(null, null, "Comments", "Additional information about the comparison", null, 200)); 752 for (StructuralMatch<Element> t : comparison.getCombined().getChildren()) { 753 addRow(gen, model.getRows(), t, comparison); 754 } 755 return gen.generate(model, prefix, 0, null); 756 } 757 758 private void addRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 759 Row r = null; 760 if (t.either() instanceof CapabilityStatementRestComponent) { 761 r = addRestRow(gen, rows, t, comparison); 762 } else if (t.either() instanceof CapabilityStatementRestSecurityComponent) { 763 r = addRestSecurityRow(gen, rows, t, comparison); 764 } else if (t.either() instanceof CapabilityStatementRestResourceComponent) { 765 r = addRestResourceRow(gen, rows, t, comparison); 766 } else if (t.either() instanceof ResourceInteractionComponent) { 767 r = addRestResourceInteractionRow(gen, rows, t, comparison); 768 } else if (t.either() instanceof CapabilityStatementRestResourceSearchParamComponent) { 769 r = addRestSearchParamRow(gen, rows, t, comparison); 770 } else if (t.either() instanceof CapabilityStatementRestResourceOperationComponent) { 771 r = addRestOperationRow(gen, rows, t, comparison); 772 } else if (t.either() instanceof CodeableConcept) { 773 r = addRestSecurityServiceRow(gen, rows, t, comparison); 774 } else if (t.either() instanceof Extension) { 775 r = addExtensionRow(gen, rows, t, comparison); 776 } else if (t.either() instanceof PrimitiveType) { 777 r = addPrimitiveTypeRow(gen, rows, t, comparison); 778 } else { 779 throw new Error("Not Done Yet: "+t.either().getClass().getName()); 780 } 781 for (StructuralMatch<Element> c : t.getChildren()) { 782 addRow(gen, r.getSubRows(), c, comparison); 783 } 784 } 785 786 private Row addRestRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 787 Row r = gen.new Row(); 788 rows.add(r); 789 r.getCells().add(gen.new Cell(null, null, "mode", null, null)); 790 CapabilityStatementRestComponent left = t.hasLeft() ? (CapabilityStatementRestComponent) t.getLeft() : null; 791 CapabilityStatementRestComponent right = t.hasRight() ? (CapabilityStatementRestComponent) t.getRight() : null; 792 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)); 793 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)); 794 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)); 795 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)); 796 r.getCells().add(cellForMessages(gen, t.getMessages())); 797 return r; 798 } 799 800 private Row addRestSecurityRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 801 Row r = gen.new Row(); 802 rows.add(r); 803 r.getCells().add(gen.new Cell(null, null, "security", null, null)); 804 CapabilityStatementRestSecurityComponent left = t.hasLeft() ? (CapabilityStatementRestSecurityComponent) t.getLeft() : null; 805 CapabilityStatementRestSecurityComponent right = t.hasRight() ? (CapabilityStatementRestSecurityComponent) t.getRight() : null; 806 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)); 807 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)); 808 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)); 809 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)); 810 r.getCells().add(cellForMessages(gen, t.getMessages())); 811 return r; 812 } 813 814 private Row addRestResourceRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 815 Row r = gen.new Row(); 816 rows.add(r); 817 r.getCells().add(gen.new Cell(null, null, "resource", null, null)); 818 CapabilityStatementRestResourceComponent left = t.hasLeft() ? (CapabilityStatementRestResourceComponent) t.getLeft() : null; 819 CapabilityStatementRestResourceComponent right = t.hasRight() ? (CapabilityStatementRestResourceComponent) t.getRight() : null; 820 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)); 821 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)); 822 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)); 823 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)); 824 r.getCells().add(cellForMessages(gen, t.getMessages())); 825 return r; 826 } 827 828 private Row addRestSearchParamRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 829 Row r = gen.new Row(); 830 rows.add(r); 831 r.getCells().add(gen.new Cell(null, null, "searchParam", null, null)); 832 CapabilityStatementRestResourceSearchParamComponent left = t.hasLeft() ? (CapabilityStatementRestResourceSearchParamComponent) t.getLeft() : null; 833 CapabilityStatementRestResourceSearchParamComponent right = t.hasRight() ? (CapabilityStatementRestResourceSearchParamComponent) t.getRight() : null; 834 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)); 835 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)); 836 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)); 837 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)); 838 r.getCells().add(cellForMessages(gen, t.getMessages())); 839 return r; 840 } 841 842 private Row addRestOperationRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 843 Row r = gen.new Row(); 844 rows.add(r); 845 r.getCells().add(gen.new Cell(null, null, "operation", null, null)); 846 CapabilityStatementRestResourceOperationComponent left = t.hasLeft() ? (CapabilityStatementRestResourceOperationComponent) t.getLeft() : null; 847 CapabilityStatementRestResourceOperationComponent right = t.hasRight() ? (CapabilityStatementRestResourceOperationComponent) t.getRight() : null; 848 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)); 849 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)); 850 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)); 851 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)); 852 r.getCells().add(cellForMessages(gen, t.getMessages())); 853 return r; 854 } 855 856 private Row addRestSecurityServiceRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 857 Row r = gen.new Row(); 858 rows.add(r); 859 r.getCells().add(gen.new Cell(null, null, "service", null, null)); 860 CodeableConcept left = t.hasLeft() ? (CodeableConcept) t.getLeft() : null; 861 CodeableConcept right = t.hasRight() ? (CodeableConcept) t.getRight() : null; 862 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)); 863 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)); 864 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)); 865 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)); 866 r.getCells().add(cellForMessages(gen, t.getMessages())); 867 return r; 868 } 869 870 private Row addRestResourceInteractionRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 871 Row r = gen.new Row(); 872 rows.add(r); 873 r.getCells().add(gen.new Cell(null, null, "interaction", null, null)); 874 ResourceInteractionComponent left = t.hasLeft() ? (ResourceInteractionComponent) t.getLeft() : null; 875 ResourceInteractionComponent right = t.hasRight() ? (ResourceInteractionComponent) t.getRight() : null; 876 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)); 877 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)); 878 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)); 879 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)); 880 r.getCells().add(cellForMessages(gen, t.getMessages())); 881 return r; 882 } 883 884 private Row addExtensionRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 885 Row r = gen.new Row(); 886 rows.add(r); 887 r.getCells().add(gen.new Cell(null, null, "expectation", null, null)); 888 Extension left = t.hasLeft() ? (Extension) t.getLeft() : null; 889 Extension right = t.hasRight() ? (Extension) t.getRight() : null; 890 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)); 891 r.getCells().add(gen.new Cell(null, null, "", null, null)); 892 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)); 893 r.getCells().add(gen.new Cell(null, null, "", null, null)); 894 r.getCells().add(cellForMessages(gen, t.getMessages())); 895 return r; 896 } 897 898 @SuppressWarnings("rawtypes") 899 private Row addPrimitiveTypeRow(HierarchicalTableGenerator gen, List<Row> rows, StructuralMatch<Element> t, CapabilityStatementComparison comparison) { 900 Row r = gen.new Row(); 901 rows.add(r); 902 r.getCells().add(gen.new Cell(null, null, t.getName(), null, null)); 903 PrimitiveType left = t.hasLeft() ? (PrimitiveType) t.getLeft() : null; 904 PrimitiveType right = t.hasRight() ? (PrimitiveType) t.getRight() : null; 905 CanonicalResource crL = left == null ? null : (CanonicalResource) session.getContextLeft().fetchResource(Resource.class, left.primitiveValue()); 906 CanonicalResource crR = right == null ? null : (CanonicalResource) session.getContextRight().fetchResource(Resource.class, right.primitiveValue()); 907 String refL = crL != null && crL.hasUserData("path") ? crL.getUserString("path") : null; 908 String dispL = crL != null && refL != null ? crL.present() : left == null ? "" : left.primitiveValue(); 909 String refR = crR != null && crR.hasUserData("path") ? crR.getUserString("path") : null; 910 String dispR = crR != null && refR != null ? crR.present() : right == null ? "" : right.primitiveValue(); 911 r.getCells().add(style(gen.new Cell(null, refL, dispL, null, null), left != null ? left.primitiveValue() : null, right != null ? right.primitiveValue() : null, true)); 912 r.getCells().add(gen.new Cell(null, null, "", null, null)); 913 r.getCells().add(style(gen.new Cell(null, refR, dispR, null, null), left != null ? left.primitiveValue() : null, right != null ? right.primitiveValue() : null, false)); 914 r.getCells().add(gen.new Cell(null, null, "", null, null)); 915 r.getCells().add(cellForMessages(gen, t.getMessages())); 916 return r; 917 } 918 919 private Cell style(Cell cell, String left, String right, boolean isLeft) { 920 if (left != null && right != null) { 921 if (!left.equals(right)) { 922 cell.setStyle("background-color: "+COLOR_DIFFERENT); 923 } 924 } else if (left != null) { 925 if (!isLeft) { 926 cell.setStyle("background-color: "+COLOR_NO_CELL_RIGHT); 927 } 928 } else if (right != null) { 929 if (isLeft) { 930 cell.setStyle("background-color: "+COLOR_NO_CELL_LEFT); 931 } 932 } 933 return cell; 934 } 935 936 @Override 937 protected String fhirType() { 938 return "CapabilityStatement"; 939 } 940 941}