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