PageSourceSearch

https://bpmn2petrinet.com/src/bpmn2petri/parser.js

js bpmn2petrinet.com collected 2026-10-06 02:44:39 UTC 12,784 bytes, 358 lines download raw bytes

1import * as BPMN from "./bpmn.js";
2
3/**
4 * Parser class to parse a BPMN diagram to a BPMN object
5 *
6 * @class Parser
7 * @property {Document} xml - The XML document to parse
8 * @property {BPMN} BPMN - The BPMN object to store the parsed BPMN diagram
9 * @property {Object} layouts - The layouts of the shapes and edges in the BPMN diagram
10 * @property {Object} nodeTypeMap - The mapping of the BPMN node types to the BPMN classes
11 */
12class Parser {
13    xml = null;
14    BPMN = new BPMN.BPMN();
15    layouts = {};
16    nodeTypeMap = {
17        startEvent: BPMN.StartEvent,
18        endEvent: BPMN.EndEvent,
19        task: BPMN.Activity,
20        lane: BPMN.Participant,
21        exclusiveGateway: BPMN.ExclusiveGateway,
22        parallelGateway: BPMN.ParallelGateway,
23        inclusiveGateway: BPMN.InclusiveGateway,
24        complexGateway: BPMN.ComplexGateway,
25        eventBasedGateway: BPMN.EventBasedGateway,
26        userTask: BPMN.UserTask,
27        businessRuleTask: BPMN.BusinessRuleTask,
28        serviceTask: BPMN.ServiceTask,
29        sendTask: BPMN.SendTask,
30        receiveTask: BPMN.ReceiveTask,
31        scriptTask: BPMN.ScriptTask,
32        manualTask: BPMN.ManualTask,
33        intermediateThrowEvent: BPMN.IntermediateThrowEvent,
34        intermediateCatchEvent: BPMN.IntermediateCatchEvent,
35        boundaryEvent: BPMN.BoundaryEvent,
36        dataObjectReference: BPMN.DataObject,
37        dataStoreReference: BPMN.DataStoreReference,
38        textAnnotation: BPMN.TextAnnotation,
39    };
40
41    constructor(xml) {
42        this.xml = xml;
43        try {
44            this.shapesLayouts = this.parseShapesLayouts();
45            this.edgesLayouts = this.parseEdgesLayouts();
46        } catch (e) {
47            console.error(e);
48            throw new Error("Error parsing BPMN diagram layouts: " + e.message);
49        }
50        try {
51            this.parseProcesses();
52        } catch (e) {
53            console.error(e);
54            throw new Error("Error parsing BPMN processes: " + e.message);
55        }
56        try {
57            this.parseMessageFlows();
58        } catch (e) {
59            console.error(e);
60            throw new Error("Error parsing BPMN message flows: " + e.message);
61        }
62    }
63
64    /**
65     * Parse the shapes layouts from the XML
66     *
67     * @returns {Object} The layouts of the shapes
68     */
69    parseShapesLayouts() {
70        const layouts = {};
71        const nodes = this.xml.getElementsByTagName("BPMNShape");
72        for (let i = 0; i < nodes.length; i++) {
73            const node = nodes[i];
74            const id = node.getAttribute("bpmnElement");
75            const bounds = node.getElementsByTagName("Bounds")[0];
76            const x = parseInt(bounds.getAttribute("x"));
77            const y = parseInt(bounds.getAttribute("y"));
78            const width = parseInt(bounds.getAttribute("width"));
79            const height = parseInt(bounds.getAttribute("height"));
80            layouts[id] = { x, y, width, height };
81        }
82        return layouts;
83    }
84
85    /**
86     * Parse the edges layouts from the XML
87     *
88     * @returns {Object} The layouts of the edges
89     */
90    parseEdgesLayouts() {
91        const layouts = {};
92        const edges = this.xml.getElementsByTagName("BPMNEdge");
93        for (let i = 0; i < edges.length; i++) {
94            const edge = edges[i];
95            const id = edge.getAttribute("bpmnElement");
96            const waypoints = edge.getElementsByTagName("waypoint");
97            const points = [];
98            for (let j = 0; j < waypoints.length; j++) {
99                const waypoint = waypoints[j];
100                const x = parseInt(waypoint.getAttribute("x"));
101                const y = parseInt(waypoint.getAttribute("y"));
102                points.push({ x, y });
103            }
104            layouts[id] = points;
105        }
106        return layouts;
107    }
108
109    /**
110     * Find the layout of a shape or edge by its ID
111     *
112     * @param {string} id - The ID of the shape or edge
113     * @returns {Object} The layout of the shape or edge
114     * @returns {null} If the layout is not found
115     */
116    findLayout(id) {
117        return this.shapesLayouts[id] || this.edgesLayouts[id];
118    }
119
120    /**
121     * Parse the processes from the XML
122     */
123    parseProcesses() {
124        const processes = this.xml.getElementsByTagName("process");
125        for (let i = 0; i < processes.length; i++) {
126            const process = processes[i];
127            const id = process.getAttribute("id");
128            const name = process.getAttribute("name");
129            const processObj = new BPMN.Process(id, name);
130
131            for (const tag in this.nodeTypeMap) {
132                this.parseNodes(process, processObj, tag);
133            }
134
135            this.parseFlows(process, processObj);
136            this.parseAssociations(process, processObj);
137            this.setNodeArcs(processObj);
138            this.BPMN.addProcess(processObj);
139        }
140    }
141
142    /**
143     * Parse the nodes from the XML
144     *
145     * @param {Element} process - The process element to parse the nodes from
146     * @param {Process} processObj - The process object to add the nodes to
147     * @param {string} tagName - The tag name of the nodes to parse
148     */
149    parseNodes(process, processObj, tagName) {
150        const elements = process.getElementsByTagName(tagName);
151        const NodeClass = this.nodeTypeMap[tagName];
152
153        for (let i = 0; i < elements.length; i++) {
154            const element = elements[i];
155
156            // 1) Salta i nodi senza id (tipicamente extensionElements come <zeebe:userTask />)
157            const id = element.getAttribute("id");
158            if (!id) continue;
159
160            const name = element.getAttribute("name") || "";
161
162            // 2) Recupera il layout; se manca, usa un fallback neutro
163            const layout = this.findLayout(id) || {
164                x: 0,
165                y: 0,
166                width: 50,
167                height: 50,
168            };
169
170            const nodeObj = new NodeClass(
171                id,
172                name,
173                layout.x,
174                layout.y,
175                layout.width,
176                layout.height,
177                processObj
178            );
179
180            processObj.addNode(nodeObj);
181
182            // Handle boundary event attachedToRef
183            const attachedToRef = element.getAttribute("attachedToRef");
184            if (attachedToRef && nodeObj instanceof BPMN.BoundaryEvent) {
185                nodeObj.attachedToRef = attachedToRef;
186                const cancelAttr = element.getAttribute("cancelActivity");
187                nodeObj.cancelActivity = cancelAttr !== "false"; // default true
188            }
189            
190            // Parse data associations (inputs/outputs) for this node
191            this.parseDataAssociations(element, nodeObj, processObj);
192        }
193    }
194
195    /**
196     * Parse data input and output associations for a node
197     *
198     * @param {Element} element - The XML element of the node
199     * @param {BPMN.Node} nodeObj - The node object
200     * @param {BPMN.Process} processObj - The process object
201     */
202    parseDataAssociations(element, nodeObj, processObj) {
203        // Parse Data Output Associations (Task -> Object)
204        const outputs = element.getElementsByTagName("dataOutputAssociation");
205        for (let i = 0; i < outputs.length; i++) {
206            const output = outputs[i];
207            const id = output.getAttribute("id");
208            const targetRefs = output.getElementsByTagName("targetRef");
209            
210            if (targetRefs.length > 0) {
211                const targetRefId = targetRefs[0].textContent;
212                const layout = this.findLayout(id) || [];
213                
214                const assoc = new BPMN.Association(
215                    nodeObj, // Source is the task/node
216                    new BPMN.BPMNNode(targetRefId), // Target is the data object
217                    id,
218                    "",
219                    layout,
220                    processObj
221                );
222                processObj.addFlow(assoc);
223            }
224        }
225
226        // Parse Data Input Associations (Object -> Task)
227        const inputs = element.getElementsByTagName("dataInputAssociation");
228        for (let i = 0; i < inputs.length; i++) {
229            const input = inputs[i];
230            const id = input.getAttribute("id");
231            const sourceRefs = input.getElementsByTagName("sourceRef");
232            
233            if (sourceRefs.length > 0) {
234                const sourceRefId = sourceRefs[0].textContent;
235                const layout = this.findLayout(id) || [];
236                
237                const assoc = new BPMN.Association(
238                    new BPMN.BPMNNode(sourceRefId), // Source is the data object
239                    nodeObj, // Target is the task/node
240                    id,
241                    "",
242                    layout,
243                    processObj
244                );
245                processObj.addFlow(assoc);
246            }
247        }
248    }
249
250    /**
251     * Parse the flows from the XML
252     *
253     * @param {Element} process - The process element to parse the flows from
254     * @param {Process} processObj - The process object to add the flows to
255     */
256    parseFlows(process, processObj) {
257        const flows = process.getElementsByTagName("sequenceFlow");
258        for (let i = 0; i < flows.length; i++) {
259            const flow = flows[i];
260            const id = flow.getAttribute("id");
261            const name = flow.getAttribute("name");
262            const sourceRef = new BPMN.BPMNNode(flow.getAttribute("sourceRef"));
263            const targetRef = new BPMN.BPMNNode(flow.getAttribute("targetRef"));
264            const layout = this.findLayout(id) || []; // se non c'è edge layout, usa []
265
266            const flowObj = new BPMN.SequenceFlow(
267                sourceRef,
268                targetRef,
269                id,
270                name,
271                layout,
272                processObj
273            );
274            processObj.addFlow(flowObj);
275        }
276    }
277
278    /**
279     * Parse the associations from the XML
280     *
281     * @param {Element} process - The process element to parse the associations from
282     * @param {Process} processObj - The process object to add the associations to
283     */
284    parseAssociations(process, processObj) {
285        const associations = process.getElementsByTagName("association");
286        for (let i = 0; i < associations.length; i++) {
287            const assoc = associations[i];
288            const id = assoc.getAttribute("id");
289            const name = assoc.getAttribute("name") || "";
290            const sourceRef = new BPMN.BPMNNode(assoc.getAttribute("sourceRef"));
291            const targetRef = new BPMN.BPMNNode(assoc.getAttribute("targetRef"));
292            const layout = this.findLayout(id) || [];
293
294            const assocObj = new BPMN.Association(
295                sourceRef,
296                targetRef,
297                id,
298                name,
299                layout,
300                processObj
301            );
302            processObj.addFlow(assocObj);
303        }
304    }
305
306    /**
307     * Set the arcs of the nodes in the process
308     *
309     * @param {Process} process - The process object to set the arcs for
310     */
311    setNodeArcs(process) {
312        const flows = process.flows;
313        for (let i = 0; i < flows.length; i++) {
314            let flow = flows[i];
315            const sourceRef = flow.source;
316            const targetRef = flow.target;
317
318            const sourceNode = process.getNodeById(sourceRef.id);
319            const targetNode = process.getNodeById(targetRef.id);
320
321            flow.source = sourceNode;
322            flow.target = targetNode;
323
324            if (sourceNode != null && targetNode != null) {
325                sourceNode.addOutArc(flow);
326                targetNode.addInArc(flow);
327            }
328        }
329    }
330
331    /**
332     * Parse the message flows from the XML
333     */
334    parseMessageFlows() {
335        const messageFlows = this.xml.getElementsByTagName("messageFlow");
336        for (let i = 0; i < messageFlows.length; i++) {
337            const messageFlow = messageFlows[i];
338            const id = messageFlow.getAttribute("id");
339            const name = messageFlow.getAttribute("name");
340            const sourceRef = messageFlow.getAttribute("sourceRef");
341            const targetRef = messageFlow.getAttribute("targetRef");
342            const source = this.BPMN.getNodeById(sourceRef);
343            const target = this.BPMN.getNodeById(targetRef);
344            const layout = this.findLayout(id) || []; // fallback
345
346            const messageFlowObj = new BPMN.MessageFlow(
347                source,
348                target,
349                id,
350                name,
351                layout
352            );
353            this.BPMN.addMessageFlow(messageFlowObj);
354        }
355    }
356}
357
358export default Parser;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.