001package org.nasdanika.html.flow; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.List; 006import java.util.Map; 007import java.util.function.BiConsumer; 008import java.util.function.Consumer; 009import java.util.stream.Collectors; 010 011import org.eclipse.emf.common.util.EList; 012import org.eclipse.emf.common.util.EMap; 013import org.eclipse.emf.common.util.URI; 014import org.eclipse.emf.ecore.EObject; 015import org.eclipse.emf.ecore.EReference; 016import org.eclipse.emf.ecore.ETypedElement; 017import org.nasdanika.common.Context; 018import org.nasdanika.common.ProgressMonitor; 019import org.nasdanika.common.Util; 020import org.nasdanika.diagram.Diagram; 021import org.nasdanika.diagram.DiagramElement; 022import org.nasdanika.flow.Artifact; 023import org.nasdanika.flow.ArtifactParticipantResponsibility; 024import org.nasdanika.flow.FlowElement; 025import org.nasdanika.flow.FlowPackage; 026import org.nasdanika.flow.Participant; 027import org.nasdanika.flow.Relationship; 028import org.nasdanika.flow.Transition; 029import org.nasdanika.flow.util.ArtifactComponentDiagramGenerator; 030import org.nasdanika.html.emf.ColumnBuilder; 031import org.nasdanika.html.model.app.Action; 032import org.nasdanika.html.model.app.AppFactory; 033import org.nasdanika.html.model.app.SectionStyle; 034import org.nasdanika.html.model.bootstrap.BootstrapFactory; 035import org.nasdanika.html.model.bootstrap.Table; 036import org.nasdanika.html.model.bootstrap.TableCell; 037import org.nasdanika.html.model.bootstrap.TableHeader; 038import org.nasdanika.html.model.bootstrap.TableRow; 039import org.nasdanika.html.model.bootstrap.TableSection; 040import org.nasdanika.ncore.NcorePackage; 041import org.nasdanika.ncore.util.NamedElementComparator; 042 043public class ArtifactActionBuilder extends ParticipantResponsibilityActionBuilder<Artifact> { 044 045 public ArtifactActionBuilder(Artifact value, Context context) { 046 super(value, context); 047 } 048 049 @Override 050 protected Action buildAction( 051 Action action, 052 BiConsumer<EObject, Action> registry, 053 Consumer<org.nasdanika.common.Consumer<org.nasdanika.html.emf.EObjectActionResolver.Context>> resolveConsumer, 054 ProgressMonitor progressMonitor) throws Exception { 055 056 action = super.buildAction(action, registry, resolveConsumer, progressMonitor); 057 EList<EObject> children = action.getChildren(); 058 for (Artifact element: getTarget().getChildren().values().stream().sorted(NamedElementComparator.INSTANCE).collect(Collectors.toList())) { 059 children.add(createChildAction(element, registry, resolveConsumer, progressMonitor)); 060 } 061 062 return action; 063 } 064 065 @Override 066 protected List<ETypedElement> getProperties() { 067 List<ETypedElement> properties = super.getProperties(); 068 properties.add(FlowPackage.Literals.ARTIFACT__INPUT_FOR); 069 properties.add(FlowPackage.Literals.ARTIFACT__OUTPUT_FOR); 070 properties.add(FlowPackage.Literals.ARTIFACT__REPOSITORIES); 071 properties.add(FlowPackage.Literals.ARTIFACT__USED_BY); 072 properties.add(FlowPackage.Literals.ARTIFACT__TEMPLATES); 073 properties.add(FlowPackage.Literals.ARTIFACT__INSTANCES); 074 return properties; 075 } 076 077 @Override 078 protected void resolve( 079 Action action, 080 org.nasdanika.html.emf.EObjectActionResolver.Context context, 081 ProgressMonitor progressMonitor) throws Exception { 082 083 super.resolve(action, context, progressMonitor); 084 085 EList<Action> sections = action.getSections(); 086 Artifact semanticElement = getTarget(); 087 if (!semanticElement.getPayloadFor().isEmpty()) { 088 sections.add(createEReferenceAction(action, FlowPackage.Literals.ARTIFACT__PAYLOAD_FOR, context, progressMonitor)); 089 } 090 091 if (!semanticElement.getResponseFor().isEmpty()) { 092 sections.add(createEReferenceAction(action, FlowPackage.Literals.ARTIFACT__RESPONSE_FOR, context, progressMonitor)); 093 } 094 095 096 // Inbound 097 EList<Relationship> inboundRelationships = semanticElement.getInboundRelationships(); 098 EMap<String, Relationship> outboundRelationships = semanticElement.getOutboundRelationships(); 099 if (!(inboundRelationships.isEmpty() && outboundRelationships.isEmpty())) { 100 Action rAction = AppFactory.eINSTANCE.createAction(); 101 rAction.setSectionStyle(SectionStyle.HEADER); 102 rAction.setText("Relationships"); 103 sections.add(rAction); 104 EList<Action> rSections = rAction.getSections(); 105 if (!inboundRelationships.isEmpty()) { 106 rSections.add(createInboundRelationshipsAction(action, context, progressMonitor)); 107 } 108 109 // Outbound 110 if (!outboundRelationships.isEmpty()) { 111 rSections.add(createOutboundRelationshipsAction(action, context, progressMonitor)); 112 } 113 } 114 115 // Activity level responsibilities 116 EList<ArtifactParticipantResponsibility> responsibilities = semanticElement.getResponsibilities(); 117 118 if (!responsibilities.isEmpty()) { 119 sections.add(createActivityResponsibilitiesAction(responsibilities, action, context, progressMonitor)); 120 } 121 122 } 123 124 private Action createActivityResponsibilitiesAction( 125 EList<ArtifactParticipantResponsibility> responsibilities, 126 Action base, 127 org.nasdanika.html.emf.EObjectActionResolver.Context context, 128 ProgressMonitor progressMonitor) throws Exception { 129 130 Action responsibilitiesAction = AppFactory.eINSTANCE.createAction(); 131 responsibilitiesAction.setText("Activity responsibilities"); 132 133 Table responsibilitiesTable = BootstrapFactory.eINSTANCE.createTable(); 134 TableHeader header = BootstrapFactory.eINSTANCE.createTableHeader(); 135 responsibilitiesTable.setHeader(header); 136 TableRow headerRow = BootstrapFactory.eINSTANCE.createTableRow(); 137 header.getRows().add(headerRow); 138 EList<TableCell> headerRowCells = headerRow.getCells(); 139 for (String title: new String[] {"Activity", "Responsible", "Accountable", "Consulted", "Informed"}) { 140 TableCell headerCell = BootstrapFactory.eINSTANCE.createTableCell(); 141 headerCell.setHeader(true); 142 headerRowCells.add(headerCell); 143 headerCell.getContent().add(createText(title)); 144 } 145 146 // Group all by container. Sort containers For each container create a row 147 Map<EObject, List<ArtifactParticipantResponsibility>> flowElementsMap = Util.groupBy(responsibilities, EObject::eContainer); 148 149 TableSection body = BootstrapFactory.eINSTANCE.createTableSection(); 150 responsibilitiesTable.setBody(body); 151 EList<TableRow> bodyRows = body.getRows(); 152 153 for (FlowElement<?> container: flowElementsMap.keySet().stream().map(e -> (FlowElement<?>) e).sorted(FlowActionBuilder::compareFlowElements).collect(Collectors.toList())) { 154 TableRow containerRow = BootstrapFactory.eINSTANCE.createTableRow(); 155 bodyRows.add(containerRow); 156 157 TableCell containerCell = BootstrapFactory.eINSTANCE.createTableCell(); 158 containerRow.getCells().add(containerCell); 159 containerCell.getContent().add(renderValue(base, null, container, context, progressMonitor)); 160 161 TableCell responsibleCell = BootstrapFactory.eINSTANCE.createTableCell(); 162 containerRow.getCells().add(responsibleCell); 163 List<ArtifactParticipantResponsibility> containerResponsibilities = flowElementsMap.get(container); 164 165 buildResponsibilityCell( 166 containerResponsibilities 167 .stream() 168 .map(ArtifactParticipantResponsibility::getResponsible) 169 .flatMap(Collection::stream) 170 .collect(Collectors.toSet()) // To remove duplicates 171 .stream() 172 .sorted(NamedElementComparator.INSTANCE) 173 .collect(Collectors.toList()), 174 responsibleCell, 175 base, 176 context, 177 progressMonitor); 178 179 TableCell accountableCell = BootstrapFactory.eINSTANCE.createTableCell(); 180 containerRow.getCells().add(accountableCell); 181 buildResponsibilityCell( 182 containerResponsibilities 183 .stream() 184 .map(ArtifactParticipantResponsibility::getAccountable) 185 .flatMap(Collection::stream) 186 .collect(Collectors.toSet()) // To remove duplicates 187 .stream() 188 .sorted(NamedElementComparator.INSTANCE) 189 .collect(Collectors.toList()), 190 accountableCell, 191 base, 192 context, 193 progressMonitor); 194 195 TableCell consultedCell = BootstrapFactory.eINSTANCE.createTableCell(); 196 containerRow.getCells().add(consultedCell); 197 buildResponsibilityCell( 198 containerResponsibilities 199 .stream() 200 .map(ArtifactParticipantResponsibility::getConsulted) 201 .flatMap(Collection::stream) 202 .collect(Collectors.toSet()) // To remove duplicates 203 .stream() 204 .sorted(NamedElementComparator.INSTANCE) 205 .collect(Collectors.toList()), 206 consultedCell, 207 base, 208 context, 209 progressMonitor); 210 211 TableCell informedCell = BootstrapFactory.eINSTANCE.createTableCell(); 212 containerRow.getCells().add(informedCell); 213 buildResponsibilityCell( 214 containerResponsibilities 215 .stream() 216 .map(ArtifactParticipantResponsibility::getInformed) 217 .flatMap(Collection::stream) 218 .collect(Collectors.toSet()) // To remove duplicates 219 .stream() 220 .sorted(NamedElementComparator.INSTANCE) 221 .collect(Collectors.toList()), 222 informedCell, 223 base, 224 context, 225 progressMonitor); 226 } 227 228 responsibilitiesAction.getContent().add(responsibilitiesTable); 229 230 return responsibilitiesAction; 231 } 232 233 private void buildResponsibilityCell( 234 List<Participant> elements, 235 TableCell cell, 236 Action base, 237 org.nasdanika.html.emf.EObjectActionResolver.Context context, 238 ProgressMonitor progressMonitor) throws Exception { 239 240 if (elements != null && !elements.isEmpty()) { 241 cell.getContent().add(renderValue(base, null, elements, context, progressMonitor)); 242 } 243 244 } 245 246 // TODO - Sort by source 247 private Action createEReferenceAction( 248 Action action, 249 EReference eReference, 250 org.nasdanika.html.emf.EObjectActionResolver.Context context, 251 ProgressMonitor progressMonitor) throws Exception { 252 Collection<ColumnBuilder<? super EObject>> columnBuilders = new ArrayList<>(); 253 columnBuilders.add(new ColumnBuilder<EObject>() { 254 255 @Override 256 public void buildHeader( 257 TableCell header, 258 Action base, 259 ETypedElement typedElement, 260 org.nasdanika.html.emf.EObjectActionResolver.Context context, 261 ProgressMonitor progressMonitor) 262 throws Exception { 263 header.getContent().add(createText("Source")); 264 } 265 266 @Override 267 public void buildCell( 268 EObject rowElement, 269 TableCell cell, 270 Action base, 271 ETypedElement typedElement, 272 org.nasdanika.html.emf.EObjectActionResolver.Context context, 273 ProgressMonitor progressMonitor) 274 throws Exception { 275 cell.getContent().add(renderValue(base, typedElement, rowElement.eContainer().eContainer(), context, progressMonitor)); 276 } 277 }); 278 279 columnBuilders.add(new ColumnBuilder<EObject>() { 280 281 @Override 282 public void buildHeader( 283 TableCell header, 284 Action base, 285 ETypedElement typedElement, 286 org.nasdanika.html.emf.EObjectActionResolver.Context context, 287 ProgressMonitor progressMonitor) 288 throws Exception { 289 header.getContent().add(createText("Key")); 290 } 291 292 @Override 293 public void buildCell( 294 EObject rowElement, 295 TableCell cell, 296 Action base, 297 ETypedElement typedElement, 298 org.nasdanika.html.emf.EObjectActionResolver.Context context, 299 ProgressMonitor progressMonitor) 300 throws Exception { 301 EObject renderedValue = renderValue(base, typedElement, ((Map.Entry<?,?>) rowElement.eContainer()).getKey(), context, progressMonitor); 302 if (renderedValue != null) { 303 cell.getContent().add(renderedValue); 304 } 305 } 306 }); 307 308 columnBuilders.add(new ColumnBuilder<EObject>() { 309 310 @Override 311 public void buildHeader( 312 TableCell header, 313 Action base, 314 ETypedElement typedElement, 315 org.nasdanika.html.emf.EObjectActionResolver.Context context, 316 ProgressMonitor progressMonitor) 317 throws Exception { 318 header.getContent().add(createText("Target")); 319 } 320 321 @Override 322 public void buildCell( 323 EObject rowElement, 324 TableCell cell, 325 Action base, 326 ETypedElement typedElement, 327 org.nasdanika.html.emf.EObjectActionResolver.Context context, 328 ProgressMonitor progressMonitor) 329 throws Exception { 330 EObject renderedValue = renderValue(base, typedElement, ((Transition) rowElement).getTarget(), context, progressMonitor); 331 if (renderedValue != null) { 332 cell.getContent().add(renderedValue); 333 } 334 } 335 }); 336 337 columnBuilders.add(new ColumnBuilder<EObject>() { 338 339 @Override 340 public void buildHeader( 341 TableCell header, 342 Action base, 343 ETypedElement typedElement, 344 org.nasdanika.html.emf.EObjectActionResolver.Context context, 345 ProgressMonitor progressMonitor) 346 throws Exception { 347 header.getContent().add(createText("Name")); 348 } 349 350 @Override 351 public void buildCell( 352 EObject rowElement, 353 TableCell cell, 354 Action base, 355 ETypedElement typedElement, 356 org.nasdanika.html.emf.EObjectActionResolver.Context context, 357 ProgressMonitor progressMonitor) 358 throws Exception { 359 EObject renderedValue = renderValue(base, typedElement, ((Transition) rowElement).getName(), context, progressMonitor); 360 if (renderedValue != null) { 361 cell.getContent().add(renderedValue); 362 } 363 } 364 }); 365 366 columnBuilders.add(new ColumnBuilder<EObject>() { 367 368 @Override 369 public void buildHeader( 370 TableCell header, 371 Action base, 372 ETypedElement typedElement, 373 org.nasdanika.html.emf.EObjectActionResolver.Context context, 374 ProgressMonitor progressMonitor) 375 throws Exception { 376 header.getContent().add(createText("Documentation")); 377 } 378 379 @Override 380 public void buildCell( 381 EObject rowElement, 382 TableCell cell, 383 Action base, 384 ETypedElement typedElement, 385 org.nasdanika.html.emf.EObjectActionResolver.Context context, 386 ProgressMonitor progressMonitor) 387 throws Exception { 388 EObject renderedValue = renderValue(base, typedElement, ((Transition) rowElement).getDocumentation(), context, progressMonitor); 389 if (renderedValue != null) { 390 cell.getContent().add(renderedValue); 391 } 392 } 393 }); 394 395 return createTableAction( 396 eReference, 397 columnBuilders, 398 action, 399 context, 400 progressMonitor); 401 } 402 403 @Override 404 protected void populateRepresentation( 405 Diagram representation, 406 Action action, 407 org.nasdanika.html.emf.EObjectActionResolver.Context context, 408 ProgressMonitor progressMonitor) throws Exception { 409 410 ArtifactComponentDiagramGenerator artifactComponentDiagramGenerator = new ArtifactComponentDiagramGenerator() { 411 412 @Override 413 protected String getArtifactLocation(Artifact semanticElement) { 414 Action elementAction = context.getAction(semanticElement); 415 if (elementAction == null) { 416 return null; 417 } 418 419 URI uri = context.resolve(elementAction, action); 420 return uri == null ? null : uri.toString(); 421 } 422 423 @Override 424 protected String getArtifactTooltip(Artifact semanticElement) { 425 Action elementAction = context.getAction(semanticElement); 426 return elementAction == null ? null : elementAction.getDescription(); 427 } 428 429 @Override 430 protected DiagramElement createDiagramElement( 431 Artifact semanticElement, 432 Map<Artifact, DiagramElement> semanticMap, 433 Artifact contextElement, 434 int depth) { 435 436 DiagramElement ret = super.createDiagramElement(semanticElement, semanticMap, contextElement, depth); 437 String text = ret.getText(); 438 int initialLineLength = 25; 439 if (text != null && text.length() > initialLineLength) { 440 ret.setText(Util.wrap(text, initialLineLength, 2, "\\n")); 441 } 442 return ret; 443 } 444 445 }; 446 447 populateRepresentation(representation, artifactComponentDiagramGenerator); 448 } 449 450 protected void populateRepresentation(Diagram representation, ArtifactComponentDiagramGenerator artifactComponentDiagramGenerator) { 451 artifactComponentDiagramGenerator.generateDiagram(getTarget(), representation); 452 } 453 454 private Action createInboundRelationshipsAction( 455 Action action, 456 org.nasdanika.html.emf.EObjectActionResolver.Context context, 457 ProgressMonitor progressMonitor) throws Exception { 458 Collection<ColumnBuilder<? super EObject>> columnBuilders = new ArrayList<>(); 459 columnBuilders.add(new ColumnBuilder<EObject>() { 460 461 @Override 462 public void buildHeader( 463 TableCell header, 464 Action base, 465 ETypedElement typedElement, 466 org.nasdanika.html.emf.EObjectActionResolver.Context context, 467 ProgressMonitor progressMonitor) 468 throws Exception { 469 header.getContent().add(createText("Source")); 470 } 471 472 @Override 473 public void buildCell( 474 EObject rowElement, 475 TableCell cell, 476 Action base, 477 ETypedElement typedElement, 478 org.nasdanika.html.emf.EObjectActionResolver.Context context, 479 ProgressMonitor progressMonitor) 480 throws Exception { 481 cell.getContent().add(renderValue(base, typedElement, rowElement.eContainer().eContainer(), context, progressMonitor)); 482 } 483 }); 484 485 columnBuilders.add(createColumnBuilder(NcorePackage.Literals.NAMED_ELEMENT__NAME)); 486 columnBuilders.add(createColumnBuilder(FlowPackage.Literals.PACKAGE_ELEMENT__DOCUMENTATION)); 487 488 Action ret = AppFactory.eINSTANCE.createAction(); 489 ret.setText("Inbound"); 490 491 Table table = buildTable( 492 getTarget().getInboundRelationships(), 493 columnBuilders, 494 action, 495 FlowPackage.Literals.ARTIFACT__INBOUND_RELATIONSHIPS, 496 context, 497 progressMonitor); 498 499 ret.getContent().add(table); 500 return ret; 501 } 502 503 private Action createOutboundRelationshipsAction( 504 Action action, 505 org.nasdanika.html.emf.EObjectActionResolver.Context context, 506 ProgressMonitor progressMonitor) throws Exception { 507 Collection<ColumnBuilder<? super Map.Entry<String, Relationship>>> columnBuilders = new ArrayList<>(); 508 columnBuilders.add(new ColumnBuilder<Map.Entry<String, Relationship>>() { 509 510 @Override 511 public void buildHeader( 512 TableCell header, 513 Action base, 514 ETypedElement typedElement, 515 org.nasdanika.html.emf.EObjectActionResolver.Context context, 516 ProgressMonitor progressMonitor) 517 throws Exception { 518 header.getContent().add(createText("Key")); 519 } 520 521 @Override 522 public void buildCell( 523 Map.Entry<String, Relationship> rowElement, 524 TableCell cell, 525 Action base, 526 ETypedElement typedElement, 527 org.nasdanika.html.emf.EObjectActionResolver.Context context, 528 ProgressMonitor progressMonitor) 529 throws Exception { 530 EObject renderedValue = renderValue(base, typedElement, rowElement.getKey(), context, progressMonitor); 531 if (renderedValue != null) { 532 cell.getContent().add(renderedValue); 533 } 534 } 535 }); 536 537 columnBuilders.add(new ColumnBuilder<Map.Entry<String, Relationship>>() { 538 539 @Override 540 public void buildHeader( 541 TableCell header, 542 Action base, 543 ETypedElement typedElement, 544 org.nasdanika.html.emf.EObjectActionResolver.Context context, 545 ProgressMonitor progressMonitor) 546 throws Exception { 547 header.getContent().add(createText("Target")); 548 } 549 550 @Override 551 public void buildCell( 552 Map.Entry<String, Relationship> rowElement, 553 TableCell cell, 554 Action base, 555 ETypedElement typedElement, 556 org.nasdanika.html.emf.EObjectActionResolver.Context context, 557 ProgressMonitor progressMonitor) 558 throws Exception { 559 Relationship relationship = rowElement.getValue(); 560 EObject renderedValue = renderValue(base, typedElement, relationship.getTarget(), context, progressMonitor); 561 if (renderedValue != null) { 562 cell.getContent().add(renderedValue); 563 } 564 } 565 }); 566 567 columnBuilders.add(new ColumnBuilder<Map.Entry<String, Relationship>>() { 568 569 @Override 570 public void buildHeader( 571 TableCell header, 572 Action base, 573 ETypedElement typedElement, 574 org.nasdanika.html.emf.EObjectActionResolver.Context context, 575 ProgressMonitor progressMonitor) 576 throws Exception { 577 header.getContent().add(createText("Name")); 578 } 579 580 @Override 581 public void buildCell( 582 Map.Entry<String, Relationship> rowElement, 583 TableCell cell, 584 Action base, 585 ETypedElement typedElement, 586 org.nasdanika.html.emf.EObjectActionResolver.Context context, 587 ProgressMonitor progressMonitor) 588 throws Exception { 589 Relationship value = rowElement.getValue(); 590 EObject renderedValue = renderValue(base, typedElement, value.getName(), context, progressMonitor); 591 if (renderedValue != null) { 592 cell.getContent().add(renderedValue); 593 } 594 } 595 }); 596 597 columnBuilders.add(new ColumnBuilder<Map.Entry<String, Relationship>>() { 598 599 @Override 600 public void buildHeader( 601 TableCell header, 602 Action base, 603 ETypedElement typedElement, 604 org.nasdanika.html.emf.EObjectActionResolver.Context context, 605 ProgressMonitor progressMonitor) 606 throws Exception { 607 header.getContent().add(createText("Documentation")); 608 } 609 610 @Override 611 public void buildCell( 612 Map.Entry<String, Relationship> rowElement, 613 TableCell cell, 614 Action base, 615 ETypedElement typedElement, 616 org.nasdanika.html.emf.EObjectActionResolver.Context context, 617 ProgressMonitor progressMonitor) 618 throws Exception { 619 Relationship value = rowElement.getValue(); 620 EObject renderedValue = renderValue(base, typedElement, value.getDocumentation(), context, progressMonitor); 621 if (renderedValue != null) { 622 cell.getContent().add(renderedValue); 623 } 624 } 625 }); 626 627 Action ret = AppFactory.eINSTANCE.createAction(); 628 ret.setText("Outbound"); 629 630 Table table = buildTable( 631 getTarget().getOutboundRelationships(), 632 columnBuilders, 633 action, 634 FlowPackage.Literals.ARTIFACT__OUTBOUND_RELATIONSHIPS, 635 context, 636 progressMonitor); 637 638 ret.getContent().add(table); 639 return ret; 640 } 641 642 643}