fix(import): handle invisible root elements

This commit ensures we pipe invisible root elements (Process,
Collaboration) through our import infrastructure, too.

This way we we receive proper events for them.

Related to #6
This commit is contained in:
Nico Rehwaldt 2014-07-01 11:33:28 +02:00
parent 921de712d2
commit ae60914146
7 changed files with 95 additions and 25 deletions

View File

@ -137,8 +137,20 @@ BpmnImporter.prototype.add = function(semantic, di, parent) {
data.label = label; data.label = label;
} }
// handle the special case that we deal with a
// invisible root element (process or collaboration)
if (di.$instanceOf('bpmndi:BPMNPlane')) {
// we still fire the added event, making sure our
// infrastructure pics it up properly
fire('add', null);
fire('added', null);
return null;
}
if (di.$instanceOf('bpmndi:BPMNShape')) {
if (di.$type === 'bpmndi:BPMNShape') {
var bounds = di.bounds; var bounds = di.bounds;
var collapsed = isCollapsed(semantic, di); var collapsed = isCollapsed(semantic, di);

View File

@ -20,6 +20,9 @@ function BpmnRegistry(events, elementRegistry) {
diagramElement: {} diagramElement: {}
}; };
// we attach to element.add rather than element.added to ensure
// the meta-data (semantic, di) for the element is already present
// during rendering
events.on('bpmn.element.add', function(e) { events.on('bpmn.element.add', function(e) {
var semantic = e.semantic, var semantic = e.semantic,
id = semantic.id; id = semantic.id;

View File

@ -1,3 +1,5 @@
module.exports = { module.exports = {
bpmnRegistry: [ 'type', require('./BpmnRegistry') ] __init__: [ 'bpmnRegistry' ],
bpmnRegistry: [ 'type', require('./BpmnRegistry') ],
bpmnImporter: [ 'type', require('./BpmnImporter') ]
}; };

View File

@ -38,6 +38,10 @@ function BpmnTreeWalker(handler) {
return gfx; return gfx;
} }
function visitRoot(element, diagram) {
return handler.root(element, diagram);
}
function visitIfDi(element, ctx) { function visitIfDi(element, ctx) {
var di = getDi(element); var di = getDi(element);
@ -96,11 +100,9 @@ function BpmnTreeWalker(handler) {
throw new Error('diagram not part of bpmn:Definitions'); throw new Error('diagram not part of bpmn:Definitions');
} }
if (!diagram) { if (!diagram && diagrams && diagrams.length) {
if (diagrams && diagrams.length) {
diagram = diagrams[0]; diagram = diagrams[0];
} }
}
// no diagram -> nothing to import // no diagram -> nothing to import
if (!diagram) { if (!diagram) {
@ -110,19 +112,23 @@ function BpmnTreeWalker(handler) {
// load DI from selected diagram only // load DI from selected diagram only
handleDiagram(diagram); handleDiagram(diagram);
var rootElement = diagram.plane.bpmnElement; var plane = diagram.plane,
rootElement = plane.bpmnElement;
if (!rootElement) { if (!rootElement) {
throw new Error('no rootElement referenced in BPMNPlane <' + diagram.plane.id + '>'); throw new Error('no rootElement referenced in BPMNPlane <' + diagram.plane.id + '>');
} }
var ctx = visitRoot(rootElement, plane);
if (is(rootElement, 'bpmn:Process')) { if (is(rootElement, 'bpmn:Process')) {
handleProcess(rootElement); handleProcess(rootElement, ctx);
} else if (is(rootElement, 'bpmn:Collaboration')) { } else if (is(rootElement, 'bpmn:Collaboration')) {
handleCollaboration(rootElement); handleCollaboration(rootElement, ctx);
// force drawing of everything not yet drawn that is part of the target DI // force drawing of everything not yet drawn that is part of the target DI
handleUnhandledProcesses(definitions.rootElements); handleUnhandledProcesses(definitions.rootElements, ctx);
} else { } else {
throw new Error('unsupported root element for bpmndi:Diagram <' + rootElement.$type + '>'); throw new Error('unsupported root element for bpmndi:Diagram <' + rootElement.$type + '>');
} }
@ -141,11 +147,12 @@ function BpmnTreeWalker(handler) {
function handleUnhandledProcesses(rootElements) { function handleUnhandledProcesses(rootElements) {
// walk through all processes that have not yet been drawn and draw them // walk through all processes that have not yet been drawn and draw them
// (in case they contain lanes with DI information) // if they contain lanes with DI information.
// we do this to pass the free-floating lane test cases in the MIWG test suite
var processes = _.filter(rootElements, function(e) { var processes = _.filter(rootElements, function(e) {
//Need this fix to get run test-case "01" return is(e, 'bpmn:Process') && e.laneSets && handledProcesses.indexOf(e) === -1;
return e.$type === 'bpmn:Process' && e.laneSets && handledProcesses.indexOf(e) === -1;
}); });
processes.forEach(contextual(handleProcess)); processes.forEach(contextual(handleProcess));
} }

View File

@ -20,6 +20,10 @@ function importBpmnDiagram(diagram, definitions, done) {
var visitor = { var visitor = {
root: function(element, di) {
return importer.add(element, di);
},
element: function(element, di, parent) { element: function(element, di, parent) {
return importer.add(element, di, parent); return importer.add(element, di, parent);
}, },

View File

@ -0,0 +1,37 @@
'use strict';
var Matchers = require('../../Matchers'),
TestHelper = require('../../TestHelper');
/* global bootstrapBpmnJS, inject */
var fs = require('fs');
var bpmnCoreModule = require('../../../../lib/core');
describe('core', function() {
beforeEach(Matchers.add);
var diagramXML = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf-8');
var testModules = [ bpmnCoreModule ];
beforeEach(bootstrapBpmnJS(diagramXML, { modules: testModules }));
describe('BpmnRegistry', function() {
it('should get process semantic by ID', inject(function(bpmnRegistry) {
expect(bpmnRegistry.getSemantic('Process_1')).toBeDefined();
}));
});
});

View File

@ -64,25 +64,18 @@ describe('import - importer', function() {
// given // given
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8'); var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
var events = []; var eventCount = 0;
// log events // log events
diagram.get('eventBus').on('bpmn.element.add', function(e) { diagram.get('eventBus').on('bpmn.element.add', function(e) {
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id }); eventCount++;
}); });
// when // when
runImport(diagram, xml, function(err, warnings) { runImport(diagram, xml, function(err, warnings) {
// then // then
expect(events).toEqual([ expect(eventCount).toEqual(7);
{ type: 'add', semantic: 'SubProcess_1', di: '_BPMNShape_SubProcess_2', diagramElement: 'SubProcess_1' },
{ type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_2', diagramElement: 'StartEvent_1' },
{ type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_2', diagramElement: 'Task_1' },
{ type: 'add', semantic: 'SequenceFlow_1', di: 'BPMNEdge_SequenceFlow_1', diagramElement: 'SequenceFlow_1' },
{ type: 'add', semantic: 'EndEvent_1', di: '_BPMNShape_EndEvent_2', diagramElement: 'EndEvent_1' },
{ type: 'add', semantic: 'SequenceFlow_2', di: 'BPMNEdge_SequenceFlow_2', diagramElement: 'SequenceFlow_2' }
]);
done(err); done(err);
}); });
@ -102,7 +95,12 @@ describe('import - importer', function() {
// log events // log events
diagram.get('eventBus').on('bpmn.element.add', function(e) { diagram.get('eventBus').on('bpmn.element.add', function(e) {
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id }); events.push({
type: 'add',
semantic: e.semantic.id,
di: e.di.id,
diagramElement: e.diagramElement && e.diagramElement.id
});
}); });
// when // when
@ -110,6 +108,7 @@ describe('import - importer', function() {
// then // then
expect(events).toEqual([ expect(events).toEqual([
{ type: 'add', semantic: 'Process_1', di: 'BPMNPlane_1', diagramElement: null },
{ type: 'add', semantic: 'SubProcess_1', di: '_BPMNShape_SubProcess_2', diagramElement: 'SubProcess_1' }, { type: 'add', semantic: 'SubProcess_1', di: '_BPMNShape_SubProcess_2', diagramElement: 'SubProcess_1' },
{ type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_2', diagramElement: 'StartEvent_1' }, { type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_2', diagramElement: 'StartEvent_1' },
{ type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_2', diagramElement: 'Task_1' }, { type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_2', diagramElement: 'Task_1' },
@ -132,7 +131,12 @@ describe('import - importer', function() {
// log events // log events
diagram.get('eventBus').on('bpmn.element.add', function(e) { diagram.get('eventBus').on('bpmn.element.add', function(e) {
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id }); events.push({
type: 'add',
semantic: e.semantic.id,
di: e.di.id,
diagramElement: e.diagramElement && e.diagramElement.id
});
}); });
// when // when
@ -140,6 +144,7 @@ describe('import - importer', function() {
// then // then
expect(events).toEqual([ expect(events).toEqual([
{ type: 'add', semantic: '_Collaboration_2', di: 'BPMNPlane_1', diagramElement: null },
{ type: 'add', semantic: 'Participant_2', di: '_BPMNShape_Participant_2', diagramElement: 'Participant_2' }, { type: 'add', semantic: 'Participant_2', di: '_BPMNShape_Participant_2', diagramElement: 'Participant_2' },
{ type: 'add', semantic: 'Lane_1', di: '_BPMNShape_Lane_2', diagramElement: 'Lane_1' }, { type: 'add', semantic: 'Lane_1', di: '_BPMNShape_Lane_2', diagramElement: 'Lane_1' },
{ type: 'add', semantic: 'Lane_2', di: '_BPMNShape_Lane_3', diagramElement: 'Lane_2' }, { type: 'add', semantic: 'Lane_2', di: '_BPMNShape_Lane_3', diagramElement: 'Lane_2' },