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 | var diagrams = definitions.diagrams;
|
162 |
|
163 | if (diagram && diagrams.indexOf(diagram) === -1) {
|
164 | throw new Error(translate('diagram not part of bpmn:Definitions'));
|
165 | }
|
166 |
|
167 | if (!diagram && diagrams && diagrams.length) {
|
168 | diagram = diagrams[0];
|
169 | }
|
170 |
|
171 |
|
172 | if (!diagram) {
|
173 | throw new Error(translate('no diagram to display'));
|
174 | }
|
175 |
|
176 |
|
177 | handleDiagram(diagram);
|
178 |
|
179 |
|
180 | var plane = diagram.plane;
|
181 |
|
182 | if (!plane) {
|
183 | throw new Error(translate(
|
184 | 'no plane for {element}',
|
185 | { element: elementToString(diagram) }
|
186 | ));
|
187 | }
|
188 |
|
189 | var rootElement = plane.bpmnElement;
|
190 |
|
191 |
|
192 |
|
193 | if (!rootElement) {
|
194 | rootElement = findDisplayCandidate(definitions);
|
195 |
|
196 | if (!rootElement) {
|
197 | throw new Error(translate('no process or collaboration to display'));
|
198 | } else {
|
199 |
|
200 | logError(
|
201 | translate('correcting missing bpmnElement on {plane} to {rootElement}', {
|
202 | plane: elementToString(plane),
|
203 | rootElement: elementToString(rootElement)
|
204 | })
|
205 | );
|
206 |
|
207 |
|
208 | plane.bpmnElement = rootElement;
|
209 | registerDi(plane);
|
210 | }
|
211 | }
|
212 |
|
213 |
|
214 | var ctx = visitRoot(rootElement, plane);
|
215 |
|
216 | if (is(rootElement, 'bpmn:Process')) {
|
217 | handleProcess(rootElement, ctx);
|
218 | } else if (is(rootElement, 'bpmn:Collaboration')) {
|
219 | handleCollaboration(rootElement, ctx);
|
220 |
|
221 |
|
222 | handleUnhandledProcesses(definitions.rootElements, ctx);
|
223 | } else {
|
224 | throw new Error(
|
225 | translate('unsupported bpmnElement for {plane}: {rootElement}', {
|
226 | plane: elementToString(plane),
|
227 | rootElement: elementToString(rootElement)
|
228 | })
|
229 | );
|
230 | }
|
231 |
|
232 |
|
233 | handleDeferred(deferred);
|
234 | }
|
235 |
|
236 | function handleDeferred() {
|
237 |
|
238 | var fn;
|
239 |
|
240 |
|
241 | while (deferred.length) {
|
242 | fn = deferred.shift();
|
243 |
|
244 | fn();
|
245 | }
|
246 | }
|
247 |
|
248 | function handleProcess(process, context) {
|
249 | handleFlowElementsContainer(process, context);
|
250 | handleIoSpecification(process.ioSpecification, context);
|
251 |
|
252 | handleArtifacts(process.artifacts, context);
|
253 |
|
254 |
|
255 | handled(process);
|
256 | }
|
257 |
|
258 | function handleUnhandledProcesses(rootElements) {
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | var processes = filter(rootElements, function(e) {
|
264 | return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets;
|
265 | });
|
266 |
|
267 | processes.forEach(contextual(handleProcess));
|
268 | }
|
269 |
|
270 | function handleMessageFlow(messageFlow, context) {
|
271 | visitIfDi(messageFlow, context);
|
272 | }
|
273 |
|
274 | function handleMessageFlows(messageFlows, context) {
|
275 | forEach(messageFlows, contextual(handleMessageFlow, context));
|
276 | }
|
277 |
|
278 | function handleDataAssociation(association, context) {
|
279 | visitIfDi(association, context);
|
280 | }
|
281 |
|
282 | function handleDataInput(dataInput, context) {
|
283 | visitIfDi(dataInput, context);
|
284 | }
|
285 |
|
286 | function handleDataOutput(dataOutput, context) {
|
287 | visitIfDi(dataOutput, context);
|
288 | }
|
289 |
|
290 | function handleArtifact(artifact, context) {
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 | visitIfDi(artifact, context);
|
297 | }
|
298 |
|
299 | function handleArtifacts(artifacts, context) {
|
300 |
|
301 | forEach(artifacts, function(e) {
|
302 | if (is(e, 'bpmn:Association')) {
|
303 | deferred.push(function() {
|
304 | handleArtifact(e, context);
|
305 | });
|
306 | } else {
|
307 | handleArtifact(e, context);
|
308 | }
|
309 | });
|
310 | }
|
311 |
|
312 | function handleIoSpecification(ioSpecification, context) {
|
313 |
|
314 | if (!ioSpecification) {
|
315 | return;
|
316 | }
|
317 |
|
318 | forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
|
319 | forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
|
320 | }
|
321 |
|
322 | function handleSubProcess(subProcess, context) {
|
323 | handleFlowElementsContainer(subProcess, context);
|
324 | handleArtifacts(subProcess.artifacts, context);
|
325 | }
|
326 |
|
327 | function handleFlowNode(flowNode, context) {
|
328 | var childCtx = visitIfDi(flowNode, context);
|
329 |
|
330 | if (is(flowNode, 'bpmn:SubProcess')) {
|
331 | handleSubProcess(flowNode, childCtx || context);
|
332 | }
|
333 |
|
334 | if (is(flowNode, 'bpmn:Activity')) {
|
335 | handleIoSpecification(flowNode.ioSpecification, context);
|
336 | }
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | deferred.push(function() {
|
346 | forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
|
347 | forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
|
348 | });
|
349 | }
|
350 |
|
351 | function handleSequenceFlow(sequenceFlow, context) {
|
352 | visitIfDi(sequenceFlow, context);
|
353 | }
|
354 |
|
355 | function handleDataElement(dataObject, context) {
|
356 | visitIfDi(dataObject, context);
|
357 | }
|
358 |
|
359 | function handleBoundaryEvent(dataObject, context) {
|
360 | visitIfDi(dataObject, context);
|
361 | }
|
362 |
|
363 | function handleLane(lane, context) {
|
364 |
|
365 | deferred.push(function() {
|
366 |
|
367 | var newContext = visitIfDi(lane, context);
|
368 |
|
369 | if (lane.childLaneSet) {
|
370 | handleLaneSet(lane.childLaneSet, newContext || context);
|
371 | }
|
372 |
|
373 | wireFlowNodeRefs(lane);
|
374 | });
|
375 | }
|
376 |
|
377 | function handleLaneSet(laneSet, context) {
|
378 | forEach(laneSet.lanes, contextual(handleLane, context));
|
379 | }
|
380 |
|
381 | function handleLaneSets(laneSets, context) {
|
382 | forEach(laneSets, contextual(handleLaneSet, context));
|
383 | }
|
384 |
|
385 | function handleFlowElementsContainer(container, context) {
|
386 | handleFlowElements(container.flowElements, context);
|
387 |
|
388 | if (container.laneSets) {
|
389 | handleLaneSets(container.laneSets, context);
|
390 | }
|
391 | }
|
392 |
|
393 | function handleFlowElements(flowElements, context) {
|
394 | forEach(flowElements, function(e) {
|
395 | if (is(e, 'bpmn:SequenceFlow')) {
|
396 | deferred.push(function() {
|
397 | handleSequenceFlow(e, context);
|
398 | });
|
399 | } else if (is(e, 'bpmn:BoundaryEvent')) {
|
400 | deferred.unshift(function() {
|
401 | handleBoundaryEvent(e, context);
|
402 | });
|
403 | } else if (is(e, 'bpmn:FlowNode')) {
|
404 | handleFlowNode(e, context);
|
405 | } else if (is(e, 'bpmn:DataObject')) {
|
406 |
|
407 | } else if (is(e, 'bpmn:DataStoreReference')) {
|
408 | handleDataElement(e, context);
|
409 | } else if (is(e, 'bpmn:DataObjectReference')) {
|
410 | handleDataElement(e, context);
|
411 | } else {
|
412 | logError(
|
413 | translate('unrecognized flowElement {element} in context {context}', {
|
414 | element: elementToString(e),
|
415 | context: (context ? elementToString(context.businessObject) : 'null')
|
416 | }),
|
417 | { element: e, context: context }
|
418 | );
|
419 | }
|
420 | });
|
421 | }
|
422 |
|
423 | function handleParticipant(participant, context) {
|
424 | var newCtx = visitIfDi(participant, context);
|
425 |
|
426 | var process = participant.processRef;
|
427 | if (process) {
|
428 | handleProcess(process, newCtx || context);
|
429 | }
|
430 | }
|
431 |
|
432 | function handleCollaboration(collaboration) {
|
433 |
|
434 | forEach(collaboration.participants, contextual(handleParticipant));
|
435 |
|
436 | handleArtifacts(collaboration.artifacts);
|
437 |
|
438 |
|
439 | deferred.push(function() {
|
440 | handleMessageFlows(collaboration.messageFlows);
|
441 | });
|
442 | }
|
443 |
|
444 |
|
445 | function wireFlowNodeRefs(lane) {
|
446 |
|
447 | forEach(lane.flowNodeRef, function(flowNode) {
|
448 | var lanes = flowNode.get('lanes');
|
449 |
|
450 | if (lanes) {
|
451 | lanes.push(lane);
|
452 | }
|
453 | });
|
454 | }
|
455 |
|
456 |
|
457 |
|
458 | return {
|
459 | handleDeferred: handleDeferred,
|
460 | handleDefinitions: handleDefinitions,
|
461 | handleSubProcess: handleSubProcess,
|
462 | registerDi: registerDi
|
463 | };
|
464 | } |
\ | No newline at end of file |