fix(util/model): able to clone Event Definitions extension elements

Related to camunda/camunda-modeler#516
Related to camunda/camunda-modeler#517
This commit is contained in:
Ricardo Matias 2017-02-02 11:28:40 +01:00 committed by Philipp Fromme
parent 1a4a8959fe
commit 2c51cfbe3d
3 changed files with 73 additions and 63 deletions

View File

@ -36,8 +36,10 @@ function ModelCloneHelper(eventBus) {
module.exports = ModelCloneHelper; module.exports = ModelCloneHelper;
ModelCloneHelper.prototype.clone = function(oldElement, newElement, properties) { ModelCloneHelper.prototype.clone = function(refElement, newElement, properties) {
this._newElement = newElement; var context = {
newElement: newElement
};
// we want the extensionElements to be cloned last // we want the extensionElements to be cloned last
// so that they can check certain properties // so that they can check certain properties
@ -46,7 +48,7 @@ ModelCloneHelper.prototype.clone = function(oldElement, newElement, properties)
}); });
forEach(properties, function(propName) { forEach(properties, function(propName) {
var oldElementProp = oldElement.get(propName), var refElementProp = refElement.get(propName),
newElementProp = newElement.get(propName), newElementProp = newElement.get(propName),
propDescriptor = newElement.$model.getPropertyDescriptor(newElement, propName), propDescriptor = newElement.$model.getPropertyDescriptor(newElement, propName),
name; name;
@ -55,22 +57,26 @@ ModelCloneHelper.prototype.clone = function(oldElement, newElement, properties)
// - same values from simple types // - same values from simple types
// - cloning id's // - cloning id's
// - cloning reference elements // - cloning reference elements
if (newElementProp === oldElementProp || if (newElementProp === refElementProp ||
(propDescriptor && (propDescriptor.isId || propDescriptor.isReference))) { (propDescriptor && (propDescriptor.isId || propDescriptor.isReference))) {
return; return;
} }
// if the property is of type 'boolean', 'string', 'number' or 'null', just set it // if the property is of type 'boolean', 'string', 'number' or 'null', just set it
if (isType(oldElementProp, [ 'boolean', 'string', 'number' ]) || oldElementProp === null) { if (isType(refElementProp, [ 'boolean', 'string', 'number' ]) || refElementProp === null) {
newElement.set(propName, oldElementProp); newElement.set(propName, refElementProp);
return; return;
} }
if (isArray(oldElementProp)) { if (isArray(refElementProp)) {
forEach(oldElementProp, function(extElement) { forEach(refElementProp, function(extElement) {
var newProp = this._deepClone(extElement); var newProp;
context.refTopLevelProperty = extElement;
newProp = this._deepClone(extElement, context);
newProp.$parent = newElement; newProp.$parent = newElement;
@ -79,21 +85,21 @@ ModelCloneHelper.prototype.clone = function(oldElement, newElement, properties)
} else { } else {
name = propName.replace(/bpmn:/, ''); name = propName.replace(/bpmn:/, '');
newElement[name] = this._deepClone(oldElementProp); context.refTopLevelProperty = refElementProp;
newElement[name] = this._deepClone(refElementProp, context);
} }
}, this); }, this);
return newElement; return newElement;
}; };
ModelCloneHelper.prototype._deepClone = function _deepClone(element) { ModelCloneHelper.prototype._deepClone = function _deepClone(propertyElement, context) {
var eventBus = this._eventBus; var eventBus = this._eventBus;
var newElement = this._newElement; var newProp = propertyElement.$model.create(propertyElement.$type);
var newProp = element.$model.create(element.$type); var properties = filter(Object.keys(propertyElement), function(prop) {
var properties = filter(Object.keys(element), function(prop) {
var descriptor = newProp.$model.getPropertyDescriptor(newProp, prop); var descriptor = newProp.$model.getPropertyDescriptor(newProp, prop);
if (descriptor && (descriptor.isId || descriptor.isReference)) { if (descriptor && (descriptor.isId || descriptor.isReference)) {
@ -111,14 +117,15 @@ ModelCloneHelper.prototype._deepClone = function _deepClone(element) {
}); });
forEach(properties, function(propName) { forEach(properties, function(propName) {
// check if the element has this property defined // check if the propertyElement has this property defined
if (element[propName] !== undefined && (element[propName].$type || isArray(element[propName]))) { if (propertyElement[propName] !== undefined &&
(propertyElement[propName].$type || isArray(propertyElement[propName]))) {
if (isArray(element[propName])) { if (isArray(propertyElement[propName])) {
newProp[propName] = []; newProp[propName] = [];
forEach(element[propName], function(property) { forEach(propertyElement[propName], function(property) {
var extProp = element.$model.getTypeDescriptor(property.$type), var extProp = propertyElement.$model.getTypeDescriptor(property.$type),
newDeepProp; newDeepProp;
// we're not going to copy undefined types // we're not going to copy undefined types
@ -127,35 +134,36 @@ ModelCloneHelper.prototype._deepClone = function _deepClone(element) {
} }
var canClone = eventBus.fire('property.clone', { var canClone = eventBus.fire('property.clone', {
newElement: newElement, newElement: context.newElement,
refTopLevelProperty: context.refTopLevelProperty,
propertyDescriptor: extProp propertyDescriptor: extProp
}); });
if (!canClone) { if (!canClone) {
// if can clone is 'undefined' or 'false' // if can clone is 'undefined' or 'false'
// check for the meta information if it is allowed // check for the meta information if it is allowed
if (element.$type === 'bpmn:ExtensionElements' && if (propertyElement.$type === 'bpmn:ExtensionElements' &&
extProp.meta && extProp.meta.allowedIn && extProp.meta && extProp.meta.allowedIn &&
!isAllowedIn(extProp, newElement.$type)) { !isAllowedIn(extProp, context.newElement.$type)) {
return false; return false;
} }
} }
newDeepProp = this._deepClone(property); newDeepProp = this._deepClone(property, context);
newDeepProp.$parent = newProp; newDeepProp.$parent = newProp;
newProp[propName].push(newDeepProp); newProp[propName].push(newDeepProp);
}, this); }, this);
} else if (element[propName].$type) { } else if (propertyElement[propName].$type) {
newProp[propName] = this._deepClone(element[propName]); newProp[propName] = this._deepClone(propertyElement[propName], context);
newProp[propName].$parent = newProp; newProp[propName].$parent = newProp;
} }
} else { } else {
// just assign directly if it's a value // just assign directly if it's a value
newProp[propName] = element[propName]; newProp[propName] = propertyElement[propName];
} }
}, this); }, this);

View File

@ -276,16 +276,14 @@ describe('util/ModelCloneHelper', function() {
newSubProcessExtElems = newSubProcess.extensionElements.values; newSubProcessExtElems = newSubProcess.extensionElements.values;
// then // then
function expectTimeCycle(extElems) { expect(intCatchEvtExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(extElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle'); expect(intCatchEvtExtElems[0].body).to.equal('foobar');
expect(extElems[0].body).to.equal('foobar');
}
expectTimeCycle(intCatchEvtExtElems); expect(startEvtExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(startEvtExtElems[0].body).to.equal('foobar');
expectTimeCycle(startEvtExtElems); expect(newSubProcessExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(newSubProcessExtElems[0].body).to.equal('foobar');
expectTimeCycle(newSubProcessExtElems);
})); }));
@ -298,10 +296,11 @@ describe('util/ModelCloneHelper', function() {
var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ connector ] }); var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ connector ] });
var msgEvtDef = moddle.create('bpmn:MessageEventDefinition'); var msgEvtDef = moddle.create('bpmn:MessageEventDefinition', {
extensionElements: extensionElements
});
var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', { var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ] eventDefinitions: [ msgEvtDef ]
}); });
@ -310,7 +309,7 @@ describe('util/ModelCloneHelper', function() {
'bpmn:eventDefinitions' 'bpmn:eventDefinitions'
]); ]);
var extElems = clonedElement.extensionElements.values; var extElems = clonedElement.eventDefinitions[0].extensionElements.values;
// then // then
expect(extElems[0].$type).to.equal('camunda:Connector'); expect(extElems[0].$type).to.equal('camunda:Connector');
@ -327,10 +326,11 @@ describe('util/ModelCloneHelper', function() {
var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ field ] }); var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ field ] });
var msgEvtDef = moddle.create('bpmn:MessageEventDefinition'); var msgEvtDef = moddle.create('bpmn:MessageEventDefinition', {
extensionElements: extensionElements
});
var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', { var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ] eventDefinitions: [ msgEvtDef ]
}); });
@ -339,7 +339,7 @@ describe('util/ModelCloneHelper', function() {
'bpmn:eventDefinitions' 'bpmn:eventDefinitions'
]); ]);
var extElems = clonedElement.extensionElements.values; var extElems = clonedElement.eventDefinitions[0].extensionElements.values;
// then // then
expect(extElems[0].$type).to.equal('camunda:Field'); expect(extElems[0].$type).to.equal('camunda:Field');
@ -356,10 +356,11 @@ describe('util/ModelCloneHelper', function() {
var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ field ] }); var extensionElements = moddle.create('bpmn:ExtensionElements', { values: [ field ] });
var msgEvtDef = moddle.create('bpmn:MessageEventDefinition'); var msgEvtDef = moddle.create('bpmn:MessageEventDefinition', {
extensionElements: extensionElements
});
var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', { var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ] eventDefinitions: [ msgEvtDef ]
}); });
@ -367,10 +368,8 @@ describe('util/ModelCloneHelper', function() {
'bpmn:extensionElements' 'bpmn:extensionElements'
]); ]);
var extElems = clonedElement.extensionElements;
// then // then
expect(extElems.values).be.empty; expect(clonedElement.eventDefinitions).to.be.empty;
})); }));
}); });

View File

@ -41,27 +41,30 @@ function CamundaModdleExtension(eventBus) {
eventBus.on('property.clone', function(context) { eventBus.on('property.clone', function(context) {
var newElement = context.newElement, var newElement = context.newElement,
refTopLevelProperty = context.refTopLevelProperty,
propDescriptor = context.propertyDescriptor; propDescriptor = context.propertyDescriptor;
if (isAllowed('camunda:FailedJobRetryTimeCycle', propDescriptor, newElement)) { return this.canCloneProperty(newElement, refTopLevelProperty, propDescriptor);
return includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') || }, this);
includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') ||
is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics');
}
if (isAllowed('camunda:Connector', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
if (isAllowed('camunda:Field', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
});
} }
CamundaModdleExtension.$inject = [ 'eventBus' ]; CamundaModdleExtension.$inject = [ 'eventBus' ];
module.exports = { CamundaModdleExtension.prototype.canCloneProperty = function(newElement, refTopLevelProperty, propDescriptor) {
__init__: [ 'CamundaModdleExtension' ],
CamundaModdleExtension: [ 'type', CamundaModdleExtension ] if (isAllowed('camunda:FailedJobRetryTimeCycle', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') ||
includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') ||
is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics');
}
if (isAllowed('camunda:Connector', propDescriptor, newElement) ||
isAllowed('camunda:Field', propDescriptor, newElement)) {
return is(refTopLevelProperty, 'bpmn:MessageEventDefinition');
}
};
module.exports = {
__init__: [ 'camundaModdleExtension' ],
camundaModdleExtension: [ 'type', CamundaModdleExtension ]
}; };