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