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