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