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