1 | import {
|
2 | filter,
|
3 | find,
|
4 | forEach
|
5 | } from 'min-dash';
|
6 |
|
7 | import Refs from 'object-refs';
|
8 |
|
9 | import {
|
10 | elementToString
|
11 | } from './Util';
|
12 |
|
13 | var diRefs = new Refs(
|
14 | { name: 'bpmnElement', enumerable: true },
|
15 | { name: 'di', configurable: true }
|
16 | );
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | function is(element, type) {
|
27 | return element.$instanceOf(type);
|
28 | }
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | function findDisplayCandidate(definitions) {
|
36 | return find(definitions.rootElements, function(e) {
|
37 | return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration');
|
38 | });
|
39 | }
|
40 |
|
41 |
|
42 | export default function BpmnTreeWalker(handler, translate) {
|
43 |
|
44 |
|
45 | var handledElements = {};
|
46 |
|
47 |
|
48 |
|
49 | var deferred = [];
|
50 |
|
51 |
|
52 |
|
53 | function contextual(fn, ctx) {
|
54 | return function(e) {
|
55 | fn(e, ctx);
|
56 | };
|
57 | }
|
58 |
|
59 | function handled(element) {
|
60 | handledElements[element.id] = element;
|
61 | }
|
62 |
|
63 | function isHandled(element) {
|
64 | return handledElements[element.id];
|
65 | }
|
66 |
|
67 | function visit(element, ctx) {
|
68 |
|
69 | var gfx = element.gfx;
|
70 |
|
71 |
|
72 | if (gfx) {
|
73 | throw new Error(
|
74 | translate('already rendered {element}', { element: elementToString(element) })
|
75 | );
|
76 | }
|
77 |
|
78 |
|
79 | return handler.element(element, ctx);
|
80 | }
|
81 |
|
82 | function visitRoot(element, diagram) {
|
83 | return handler.root(element, diagram);
|
84 | }
|
85 |
|
86 | function visitIfDi(element, ctx) {
|
87 |
|
88 | try {
|
89 | var gfx = element.di && visit(element, ctx);
|
90 |
|
91 | handled(element);
|
92 |
|
93 | return gfx;
|
94 | } catch (e) {
|
95 | logError(e.message, { element: element, error: e });
|
96 |
|
97 | console.error(translate('failed to import {element}', { element: elementToString(element) }));
|
98 | console.error(e);
|
99 | }
|
100 | }
|
101 |
|
102 | function logError(message, context) {
|
103 | handler.error(message, context);
|
104 | }
|
105 |
|
106 |
|
107 |
|
108 | function registerDi(di) {
|
109 | var bpmnElement = di.bpmnElement;
|
110 |
|
111 | if (bpmnElement) {
|
112 | if (bpmnElement.di) {
|
113 | logError(
|
114 | translate('multiple DI elements defined for {element}', {
|
115 | element: elementToString(bpmnElement)
|
116 | }),
|
117 | { element: bpmnElement }
|
118 | );
|
119 | } else {
|
120 | diRefs.bind(bpmnElement, 'di');
|
121 | bpmnElement.di = di;
|
122 | }
|
123 | } else {
|
124 | logError(
|
125 | translate('no bpmnElement referenced in {element}', {
|
126 | element: elementToString(di)
|
127 | }),
|
128 | { element: di }
|
129 | );
|
130 | }
|
131 | }
|
132 |
|
133 | function handleDiagram(diagram) {
|
134 | handlePlane(diagram.plane);
|
135 | }
|
136 |
|
137 | function handlePlane(plane) {
|
138 | registerDi(plane);
|
139 |
|
140 | forEach(plane.planeElement, handlePlaneElement);
|
141 | }
|
142 |
|
143 | function handlePlaneElement(planeElement) {
|
144 | registerDi(planeElement);
|
145 | }
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | |
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | function handleDefinitions(definitions, diagram) {
|
159 |
|
160 |
|
161 |
|
162 | var diagrams = definitions.diagrams;
|
163 |
|
164 | if (diagram && diagrams.indexOf(diagram) === -1) {
|
165 | throw new Error(translate('diagram not part of bpmn:Definitions'));
|
166 | }
|
167 |
|
168 | if (!diagram && diagrams && diagrams.length) {
|
169 | diagram = diagrams[0];
|
170 | }
|
171 |
|
172 |
|
173 | if (!diagram) {
|
174 | throw new Error(translate('no diagram to display'));
|
175 | }
|
176 |
|
177 |
|
178 | handleDiagram(diagram);
|
179 |
|
180 |
|
181 | var plane = diagram.plane;
|
182 |
|
183 | if (!plane) {
|
184 | throw new Error(translate(
|
185 | 'no plane for {element}',
|
186 | { element: elementToString(diagram) }
|
187 | ));
|
188 | }
|
189 |
|
190 | var rootElement = plane.bpmnElement;
|
191 |
|
192 |
|
193 |
|
194 | if (!rootElement) {
|
195 | rootElement = findDisplayCandidate(definitions);
|
196 |
|
197 | if (!rootElement) {
|
198 | throw new Error(translate('no process or collaboration to display'));
|
199 | } else {
|
200 |
|
201 | logError(
|
202 | translate('correcting missing bpmnElement on {plane} to {rootElement}', {
|
203 | plane: elementToString(plane),
|
204 | rootElement: elementToString(rootElement)
|
205 | })
|
206 | );
|
207 |
|
208 |
|
209 | plane.bpmnElement = rootElement;
|
210 | registerDi(plane);
|
211 | }
|
212 | }
|
213 |
|
214 |
|
215 | var ctx = visitRoot(rootElement, plane);
|
216 |
|
217 | if (is(rootElement, 'bpmn:Process')) {
|
218 | handleProcess(rootElement, ctx);
|
219 | } else if (is(rootElement, 'bpmn:Collaboration')) {
|
220 | handleCollaboration(rootElement, ctx);
|
221 |
|
222 |
|
223 | handleUnhandledProcesses(definitions.rootElements, ctx);
|
224 | } else {
|
225 | throw new Error(
|
226 | translate('unsupported bpmnElement for {plane}: {rootElement}', {
|
227 | plane: elementToString(plane),
|
228 | rootElement: elementToString(rootElement)
|
229 | })
|
230 | );
|
231 | }
|
232 |
|
233 |
|
234 | handleDeferred(deferred);
|
235 | }
|
236 |
|
237 | function handleDeferred() {
|
238 |
|
239 | var fn;
|
240 |
|
241 |
|
242 | while (deferred.length) {
|
243 | fn = deferred.shift();
|
244 |
|
245 | fn();
|
246 | }
|
247 | }
|
248 |
|
249 | function handleProcess(process, context) {
|
250 | handleFlowElementsContainer(process, context);
|
251 | handleIoSpecification(process.ioSpecification, context);
|
252 |
|
253 | handleArtifacts(process.artifacts, context);
|
254 |
|
255 |
|
256 | handled(process);
|
257 | }
|
258 |
|
259 | function handleUnhandledProcesses(rootElements, ctx) {
|
260 |
|
261 |
|
262 |
|
263 |
|
264 | var processes = filter(rootElements, function(e) {
|
265 | return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets;
|
266 | });
|
267 |
|
268 | processes.forEach(contextual(handleProcess, ctx));
|
269 | }
|
270 |
|
271 | function handleMessageFlow(messageFlow, context) {
|
272 | visitIfDi(messageFlow, context);
|
273 | }
|
274 |
|
275 | function handleMessageFlows(messageFlows, context) {
|
276 | forEach(messageFlows, contextual(handleMessageFlow, context));
|
277 | }
|
278 |
|
279 | function handleDataAssociation(association, context) {
|
280 | visitIfDi(association, context);
|
281 | }
|
282 |
|
283 | function handleDataInput(dataInput, context) {
|
284 | visitIfDi(dataInput, context);
|
285 | }
|
286 |
|
287 | function handleDataOutput(dataOutput, context) {
|
288 | visitIfDi(dataOutput, context);
|
289 | }
|
290 |
|
291 | function handleArtifact(artifact, context) {
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | visitIfDi(artifact, context);
|
298 | }
|
299 |
|
300 | function handleArtifacts(artifacts, context) {
|
301 |
|
302 | forEach(artifacts, function(e) {
|
303 | if (is(e, 'bpmn:Association')) {
|
304 | deferred.push(function() {
|
305 | handleArtifact(e, context);
|
306 | });
|
307 | } else {
|
308 | handleArtifact(e, context);
|
309 | }
|
310 | });
|
311 | }
|
312 |
|
313 | function handleIoSpecification(ioSpecification, context) {
|
314 |
|
315 | if (!ioSpecification) {
|
316 | return;
|
317 | }
|
318 |
|
319 | forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
|
320 | forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
|
321 | }
|
322 |
|
323 | function handleSubProcess(subProcess, context) {
|
324 | handleFlowElementsContainer(subProcess, context);
|
325 | handleArtifacts(subProcess.artifacts, context);
|
326 | }
|
327 |
|
328 | function handleFlowNode(flowNode, context) {
|
329 | var childCtx = visitIfDi(flowNode, context);
|
330 |
|
331 | if (is(flowNode, 'bpmn:SubProcess')) {
|
332 | handleSubProcess(flowNode, childCtx || context);
|
333 | }
|
334 |
|
335 | if (is(flowNode, 'bpmn:Activity')) {
|
336 | handleIoSpecification(flowNode.ioSpecification, context);
|
337 | }
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | deferred.push(function() {
|
347 | forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
|
348 | forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
|
349 | });
|
350 | }
|
351 |
|
352 | function handleSequenceFlow(sequenceFlow, context) {
|
353 | visitIfDi(sequenceFlow, context);
|
354 | }
|
355 |
|
356 | function handleDataElement(dataObject, context) {
|
357 | visitIfDi(dataObject, context);
|
358 | }
|
359 |
|
360 | function handleLane(lane, context) {
|
361 |
|
362 | deferred.push(function() {
|
363 |
|
364 | var newContext = visitIfDi(lane, context);
|
365 |
|
366 | if (lane.childLaneSet) {
|
367 | handleLaneSet(lane.childLaneSet, newContext || context);
|
368 | }
|
369 |
|
370 | wireFlowNodeRefs(lane);
|
371 | });
|
372 | }
|
373 |
|
374 | function handleLaneSet(laneSet, context) {
|
375 | forEach(laneSet.lanes, contextual(handleLane, context));
|
376 | }
|
377 |
|
378 | function handleLaneSets(laneSets, context) {
|
379 | forEach(laneSets, contextual(handleLaneSet, context));
|
380 | }
|
381 |
|
382 | function handleFlowElementsContainer(container, context) {
|
383 | handleFlowElements(container.flowElements, context);
|
384 |
|
385 | if (container.laneSets) {
|
386 | handleLaneSets(container.laneSets, context);
|
387 | }
|
388 | }
|
389 |
|
390 | function handleFlowElements(flowElements, context) {
|
391 | forEach(flowElements, function(e) {
|
392 | if (is(e, 'bpmn:SequenceFlow')) {
|
393 | deferred.push(function() {
|
394 | handleSequenceFlow(e, context);
|
395 | });
|
396 | } else if (is(e, 'bpmn:BoundaryEvent')) {
|
397 | deferred.unshift(function() {
|
398 | handleFlowNode(e, context);
|
399 | });
|
400 | } else if (is(e, 'bpmn:FlowNode')) {
|
401 | handleFlowNode(e, context);
|
402 | } else if (is(e, 'bpmn:DataObject')) {
|
403 |
|
404 |
|
405 | } else if (is(e, 'bpmn:DataStoreReference')) {
|
406 | handleDataElement(e, context);
|
407 | } else if (is(e, 'bpmn:DataObjectReference')) {
|
408 | handleDataElement(e, context);
|
409 | } else {
|
410 | logError(
|
411 | translate('unrecognized flowElement {element} in context {context}', {
|
412 | element: elementToString(e),
|
413 | context: (context ? elementToString(context.businessObject) : 'null')
|
414 | }),
|
415 | { element: e, context: context }
|
416 | );
|
417 | }
|
418 | });
|
419 | }
|
420 |
|
421 | function handleParticipant(participant, context) {
|
422 | var newCtx = visitIfDi(participant, context);
|
423 |
|
424 | var process = participant.processRef;
|
425 | if (process) {
|
426 | handleProcess(process, newCtx || context);
|
427 | }
|
428 | }
|
429 |
|
430 | function handleCollaboration(collaboration) {
|
431 |
|
432 | forEach(collaboration.participants, contextual(handleParticipant));
|
433 |
|
434 | handleArtifacts(collaboration.artifacts);
|
435 |
|
436 |
|
437 | deferred.push(function() {
|
438 | handleMessageFlows(collaboration.messageFlows);
|
439 | });
|
440 | }
|
441 |
|
442 |
|
443 | function wireFlowNodeRefs(lane) {
|
444 |
|
445 |
|
446 | forEach(lane.flowNodeRef, function(flowNode) {
|
447 | var lanes = flowNode.get('lanes');
|
448 |
|
449 | if (lanes) {
|
450 | lanes.push(lane);
|
451 | }
|
452 | });
|
453 | }
|
454 |
|
455 |
|
456 |
|
457 | return {
|
458 | handleDeferred: handleDeferred,
|
459 | handleDefinitions: handleDefinitions,
|
460 | handleSubProcess: handleSubProcess,
|
461 | registerDi: registerDi
|
462 | };
|
463 | } |
\ | No newline at end of file |