2014-03-13 15:06:30 +00:00
|
|
|
var _ = require('lodash');
|
|
|
|
|
2014-06-23 12:44:03 +00:00
|
|
|
function BpmnTreeWalker(handler) {
|
2014-03-13 15:06:30 +00:00
|
|
|
|
|
|
|
var elementDiMap = {};
|
|
|
|
var elementGfxMap = {};
|
|
|
|
|
2014-03-22 00:47:03 +00:00
|
|
|
// list of containers already walked
|
|
|
|
var handledProcesses = [];
|
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
///// Helpers /////////////////////////////////
|
|
|
|
|
|
|
|
function contextual(fn, ctx) {
|
|
|
|
return function(e) {
|
|
|
|
fn(e, ctx);
|
|
|
|
};
|
|
|
|
}
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
function is(element, type) {
|
2014-04-28 09:09:47 +00:00
|
|
|
return element.$instanceOf(type);
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function visit(element, di, ctx) {
|
|
|
|
|
2014-03-22 00:47:03 +00:00
|
|
|
var gfx = elementGfxMap[element.id];
|
|
|
|
|
|
|
|
// avoid multiple rendering of elements
|
|
|
|
if (gfx) {
|
2014-06-23 12:44:03 +00:00
|
|
|
throw new Error('already rendered <' + element.id + '>');
|
2014-03-22 00:47:03 +00:00
|
|
|
}
|
|
|
|
|
2014-03-21 15:46:56 +00:00
|
|
|
// call handler
|
2014-03-22 00:47:03 +00:00
|
|
|
gfx = handler.element(element, di, ctx);
|
2014-03-13 15:06:30 +00:00
|
|
|
|
|
|
|
// and log returned result
|
|
|
|
elementGfxMap[element.id] = gfx;
|
|
|
|
|
|
|
|
return gfx;
|
|
|
|
}
|
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
function visitIfDi(element, ctx) {
|
|
|
|
var di = getDi(element);
|
|
|
|
|
|
|
|
if (di) {
|
|
|
|
return visit(element, di, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-21 15:46:56 +00:00
|
|
|
function logError(message, context) {
|
|
|
|
handler.error(message, context);
|
|
|
|
}
|
2014-03-13 19:21:42 +00:00
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
////// DI handling ////////////////////////////
|
|
|
|
|
|
|
|
function buildDiMap(definitions) {
|
|
|
|
_.forEach(definitions.diagrams, handleDiagram);
|
|
|
|
}
|
|
|
|
|
|
|
|
function registerDi(element) {
|
|
|
|
var bpmnElement = element.bpmnElement;
|
2014-03-21 15:46:56 +00:00
|
|
|
if (bpmnElement) {
|
|
|
|
elementDiMap[bpmnElement.id] = element;
|
|
|
|
} else {
|
|
|
|
logError('no bpmnElement for <' + element.$type + '#' + element.id + '>', { element: element });
|
|
|
|
}
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getDi(bpmnElement) {
|
|
|
|
var id = bpmnElement.id;
|
|
|
|
return id ? elementDiMap[id] : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleDiagram(diagram) {
|
|
|
|
handlePlane(diagram.plane);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handlePlane(plane) {
|
|
|
|
registerDi(plane);
|
|
|
|
|
|
|
|
_.forEach(plane.planeElement, handlePlaneElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handlePlaneElement(planeElement) {
|
|
|
|
registerDi(planeElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////// Semantic handling //////////////////////
|
|
|
|
|
|
|
|
function handleDefinitions(definitions, diagram) {
|
|
|
|
// make sure we walk the correct bpmnElement
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
var diagrams = definitions.diagrams;
|
|
|
|
|
|
|
|
if (diagram && diagrams.indexOf(diagram) === -1) {
|
|
|
|
throw new Error('diagram not part of bpmn:Definitions');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!diagram) {
|
2014-03-17 10:11:25 +00:00
|
|
|
if (diagrams && diagrams.length) {
|
|
|
|
diagram = diagrams[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no diagram -> nothing to import
|
|
|
|
if (!diagram) {
|
|
|
|
return;
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
2014-03-22 00:47:03 +00:00
|
|
|
// load DI from selected diagram only
|
|
|
|
handleDiagram(diagram);
|
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
var rootElement = diagram.plane.bpmnElement;
|
|
|
|
|
2014-05-23 14:31:28 +00:00
|
|
|
if (!rootElement) {
|
|
|
|
throw new Error('no rootElement referenced in BPMNPlane <' + diagram.plane.id + '>');
|
|
|
|
}
|
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
if (is(rootElement, 'bpmn:Process')) {
|
|
|
|
handleProcess(rootElement);
|
|
|
|
} else
|
|
|
|
if (is(rootElement, 'bpmn:Collaboration')) {
|
|
|
|
handleCollaboration(rootElement);
|
2014-03-22 00:47:03 +00:00
|
|
|
|
|
|
|
// force drawing of everything not yet drawn that is part of the target DI
|
|
|
|
handleUnhandledProcesses(definitions.rootElements);
|
2014-03-13 15:06:30 +00:00
|
|
|
} else {
|
2014-03-27 15:50:31 +00:00
|
|
|
throw new Error('unsupported root element for bpmndi:Diagram <' + rootElement.$type + '>');
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 00:47:03 +00:00
|
|
|
function handleUnhandledProcesses(rootElements) {
|
|
|
|
|
|
|
|
// walk through all processes that have not yet been drawn and draw them
|
|
|
|
// (in case they contain lanes with DI information)
|
2014-06-23 12:44:03 +00:00
|
|
|
var processes = _.filter(rootElements, function(e) {
|
|
|
|
return e.$type === 'bpmn:Process' && e.laneSets && handledProcesses.indexOf(e) === -1;
|
2014-03-22 00:47:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
processes.forEach(contextual(handleProcess));
|
|
|
|
}
|
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
function handleDataAssociation(association, context) {
|
|
|
|
visitIfDi(association, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleDataInput(dataInput, context) {
|
|
|
|
visitIfDi(dataInput, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleDataOutput(dataOutput, context) {
|
|
|
|
visitIfDi(dataOutput, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleArtifact(artifact, context) {
|
|
|
|
|
|
|
|
// bpmn:TextAnnotation
|
|
|
|
// bpmn:Group
|
|
|
|
// bpmn:Association
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
visitIfDi(artifact, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleArtifacts(artifacts, context) {
|
|
|
|
_.forEach(artifacts, contextual(handleArtifact, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleIoSpecification(ioSpecification, context) {
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
if (!ioSpecification) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_.forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
|
|
|
|
_.forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleSubProcess(subProcess, context) {
|
|
|
|
handleFlowElementsContainer(subProcess, context);
|
|
|
|
handleArtifacts(subProcess.artifacts, context);
|
|
|
|
}
|
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
function handleFlowNode(flowNode, context) {
|
2014-03-13 19:21:42 +00:00
|
|
|
var childCtx = visitIfDi(flowNode, context);
|
2014-03-13 15:06:30 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
if (is(flowNode, 'bpmn:SubProcess')) {
|
|
|
|
handleSubProcess(flowNode, childCtx || context);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(flowNode, 'bpmn:Activity')) {
|
|
|
|
_.forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, null));
|
|
|
|
_.forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, null));
|
2014-03-13 15:06:30 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
handleIoSpecification(flowNode.ioSpecification, context);
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleSequenceFlow(sequenceFlow, context) {
|
2014-03-13 19:21:42 +00:00
|
|
|
visitIfDi(sequenceFlow, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleDataElement(dataObject, context) {
|
|
|
|
visitIfDi(dataObject, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleLane(lane, context) {
|
|
|
|
var newContext = visitIfDi(lane, context);
|
|
|
|
|
|
|
|
if (lane.childLaneSet) {
|
|
|
|
handleLaneSet(lane.childLaneSet, newContext || context);
|
|
|
|
} else {
|
|
|
|
handleFlowElements(lane.flowNodeRef, newContext || context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleLaneSet(laneSet, context) {
|
|
|
|
_.forEach(laneSet.lanes, contextual(handleLane, context));
|
|
|
|
}
|
2014-03-13 15:06:30 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
function handleLaneSets(laneSets, context) {
|
|
|
|
_.forEach(laneSets, contextual(handleLaneSet, context));
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleFlowElementsContainer(container, context) {
|
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
if (container.laneSets) {
|
|
|
|
handleLaneSets(container.laneSets, context);
|
|
|
|
handleNonFlowNodes(container.flowElements);
|
|
|
|
} else {
|
|
|
|
handleFlowElements(container.flowElements, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleNonFlowNodes(flowElements, context) {
|
|
|
|
var sequenceFlows = [];
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
_.forEach(flowElements, function(e) {
|
|
|
|
if (is(e, 'bpmn:SequenceFlow')) {
|
|
|
|
sequenceFlows.push(e);
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataObject')) {
|
|
|
|
// SKIP (assume correct referencing via DataObjectReference)
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataStoreReference')) {
|
|
|
|
handleDataElement(e, context);
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataObjectReference')) {
|
|
|
|
handleDataElement(e, context);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// handle SequenceFlows
|
|
|
|
_.forEach(sequenceFlows, contextual(handleSequenceFlow, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleFlowElements(flowElements, context) {
|
2014-03-13 15:06:30 +00:00
|
|
|
var sequenceFlows = [];
|
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
_.forEach(flowElements, function(e) {
|
2014-03-13 15:06:30 +00:00
|
|
|
if (is(e, 'bpmn:SequenceFlow')) {
|
|
|
|
sequenceFlows.push(e);
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:FlowNode')) {
|
|
|
|
handleFlowNode(e, context);
|
2014-03-13 19:21:42 +00:00
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataObject')) {
|
|
|
|
// SKIP (assume correct referencing via DataObjectReference)
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataStoreReference')) {
|
|
|
|
handleDataElement(e, context);
|
|
|
|
} else
|
|
|
|
if (is(e, 'bpmn:DataObjectReference')) {
|
|
|
|
handleDataElement(e, context);
|
2014-03-13 15:06:30 +00:00
|
|
|
} else {
|
2014-06-23 12:44:03 +00:00
|
|
|
logError(
|
|
|
|
'unrecognized flowElement <' + e.$type + '> in context ' + (context ? context.id : null),
|
|
|
|
{ element: e, context: context });
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// handle SequenceFlows
|
|
|
|
_.forEach(sequenceFlows, contextual(handleSequenceFlow, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleParticipant(participant, context) {
|
2014-03-13 19:21:42 +00:00
|
|
|
var newCtx = visitIfDi(participant, context);
|
2014-03-13 15:06:30 +00:00
|
|
|
|
|
|
|
var process = participant.processRef;
|
|
|
|
if (process) {
|
2014-03-13 19:21:42 +00:00
|
|
|
handleProcess(process, newCtx || context);
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleProcess(process, context) {
|
|
|
|
handleFlowElementsContainer(process, context);
|
2014-03-13 19:21:42 +00:00
|
|
|
handleIoSpecification(process.ioSpecification, context);
|
|
|
|
|
|
|
|
handleArtifacts(process.artifacts, context);
|
2014-03-22 00:47:03 +00:00
|
|
|
|
|
|
|
// log process handled
|
|
|
|
handledProcesses.push(process);
|
2014-03-13 19:21:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleMessageFlow(messageFlow, context) {
|
|
|
|
visitIfDi(messageFlow, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleMessageFlows(messageFlows, context) {
|
|
|
|
if (messageFlows) {
|
|
|
|
_.forEach(messageFlows, contextual(handleMessageFlow, context));
|
|
|
|
}
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleCollaboration(collaboration) {
|
|
|
|
|
|
|
|
_.forEach(collaboration.participants, contextual(handleParticipant));
|
|
|
|
|
2014-03-13 19:21:42 +00:00
|
|
|
handleArtifacts(collaboration.artifacts);
|
|
|
|
|
|
|
|
handleMessageFlows(collaboration.messageFlows);
|
2014-03-13 15:06:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///// API ////////////////////////////////
|
2014-04-28 09:09:47 +00:00
|
|
|
|
2014-03-13 15:06:30 +00:00
|
|
|
return {
|
|
|
|
handleDefinitions: handleDefinitions
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-06-23 12:44:03 +00:00
|
|
|
module.exports = BpmnTreeWalker;
|