Importer.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import {
  2. find,
  3. forEach,
  4. map
  5. } from 'min-dash';
  6. import BpmnTreeWalker from './BpmnTreeWalker';
  7. import { is } from '../util/ModelUtil';
  8. /**
  9. * @typedef {import('../model/Types').ModdleElement} ModdleElement
  10. *
  11. * @typedef { {
  12. * warnings: string[];
  13. * } } ImportBPMNDiagramResult
  14. *
  15. * @typedef {ImportBPMNDiagramResult & Error} ImportBPMNDiagramError
  16. */
  17. /**
  18. * Import the definitions into a diagram.
  19. *
  20. * Errors and warnings are reported through the specified callback.
  21. *
  22. * @param {ModdleElement} diagram
  23. * @param {ModdleElement} definitions
  24. * @param {ModdleElement} [bpmnDiagram] The diagram to be rendered (if not
  25. * provided, the first one will be rendered).
  26. *
  27. * @return {Promise<ImportBPMNDiagramResult>}
  28. */
  29. export function importBpmnDiagram(diagram, definitions, bpmnDiagram) {
  30. var importer,
  31. eventBus,
  32. translate,
  33. canvas;
  34. var error,
  35. warnings = [];
  36. /**
  37. * Walk the diagram semantically, importing (=drawing)
  38. * all elements you encounter.
  39. *
  40. * @param {ModdleElement} definitions
  41. * @param {ModdleElement} bpmnDiagram
  42. */
  43. function render(definitions, bpmnDiagram) {
  44. var visitor = {
  45. root: function(element, di) {
  46. return importer.add(element, di);
  47. },
  48. element: function(element, di, parentShape) {
  49. return importer.add(element, di, parentShape);
  50. },
  51. error: function(message, context) {
  52. warnings.push({ message: message, context: context });
  53. }
  54. };
  55. var walker = new BpmnTreeWalker(visitor, translate);
  56. bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]);
  57. var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram);
  58. if (!diagramsToImport) {
  59. throw new Error(translate('no diagram to display'));
  60. }
  61. // traverse BPMN 2.0 document model,
  62. // starting at definitions
  63. forEach(diagramsToImport, function(diagram) {
  64. walker.handleDefinitions(definitions, diagram);
  65. });
  66. var rootId = bpmnDiagram.plane.bpmnElement.id;
  67. // we do need to account for different ways we create root elements
  68. // each nested imported <root> do have the `_plane` suffix, while
  69. // the root <root> is found under the business object ID
  70. canvas.setRootElement(
  71. canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId)
  72. );
  73. }
  74. return new Promise(function(resolve, reject) {
  75. try {
  76. importer = diagram.get('bpmnImporter');
  77. eventBus = diagram.get('eventBus');
  78. translate = diagram.get('translate');
  79. canvas = diagram.get('canvas');
  80. eventBus.fire('import.render.start', { definitions: definitions });
  81. render(definitions, bpmnDiagram);
  82. eventBus.fire('import.render.complete', {
  83. error: error,
  84. warnings: warnings
  85. });
  86. return resolve({ warnings: warnings });
  87. } catch (e) {
  88. e.warnings = warnings;
  89. return reject(e);
  90. }
  91. });
  92. }
  93. /**
  94. * Returns all diagrams in the same hierarchy as the requested diagram.
  95. * Includes all parent and sub process diagrams.
  96. *
  97. * @param {ModdleElement} definitions
  98. * @param {ModdleElement} bpmnDiagram
  99. *
  100. * @return {ModdleElement[]}
  101. */
  102. function getDiagramsToImport(definitions, bpmnDiagram) {
  103. if (!bpmnDiagram) {
  104. return;
  105. }
  106. var bpmnElement = bpmnDiagram.plane.bpmnElement,
  107. rootElement = bpmnElement;
  108. if (!is(bpmnElement, 'bpmn:Process') && !is(bpmnElement, 'bpmn:Collaboration')) {
  109. rootElement = findRootProcess(bpmnElement);
  110. }
  111. // in case the process is part of a collaboration, the plane references the
  112. // collaboration, not the process
  113. var collaboration;
  114. if (is(rootElement, 'bpmn:Collaboration')) {
  115. collaboration = rootElement;
  116. } else {
  117. collaboration = find(definitions.rootElements, function(element) {
  118. if (!is(element, 'bpmn:Collaboration')) {
  119. return;
  120. }
  121. return find(element.participants, function(participant) {
  122. return participant.processRef === rootElement;
  123. });
  124. });
  125. }
  126. var rootElements = [ rootElement ];
  127. // all collaboration processes can contain sub-diagrams
  128. if (collaboration) {
  129. rootElements = map(collaboration.participants, function(participant) {
  130. return participant.processRef;
  131. });
  132. rootElements.push(collaboration);
  133. }
  134. var allChildren = selfAndAllFlowElements(rootElements);
  135. // if we have multiple diagrams referencing the same element, we
  136. // use the first in the file
  137. var diagramsToImport = [ bpmnDiagram ];
  138. var handledElements = [ bpmnElement ];
  139. forEach(definitions.diagrams, function(diagram) {
  140. var businessObject = diagram.plane.bpmnElement;
  141. if (
  142. allChildren.indexOf(businessObject) !== -1 &&
  143. handledElements.indexOf(businessObject) === -1
  144. ) {
  145. diagramsToImport.push(diagram);
  146. handledElements.push(businessObject);
  147. }
  148. });
  149. return diagramsToImport;
  150. }
  151. function selfAndAllFlowElements(elements) {
  152. var result = [];
  153. forEach(elements, function(element) {
  154. if (!element) {
  155. return;
  156. }
  157. result.push(element);
  158. result = result.concat(selfAndAllFlowElements(element.flowElements));
  159. });
  160. return result;
  161. }
  162. function findRootProcess(element) {
  163. var parent = element;
  164. while (parent) {
  165. if (is(parent, 'bpmn:Process')) {
  166. return parent;
  167. }
  168. parent = parent.$parent;
  169. }
  170. }