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

View File

@ -276,16 +276,14 @@ describe('util/ModelCloneHelper', function() {
newSubProcessExtElems = newSubProcess.extensionElements.values;
// then
function expectTimeCycle(extElems) {
expect(extElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(extElems[0].body).to.equal('foobar');
}
expect(intCatchEvtExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(intCatchEvtExtElems[0].body).to.equal('foobar');
expectTimeCycle(intCatchEvtExtElems);
expect(startEvtExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(startEvtExtElems[0].body).to.equal('foobar');
expectTimeCycle(startEvtExtElems);
expectTimeCycle(newSubProcessExtElems);
expect(newSubProcessExtElems[0].$type).to.equal('camunda:FailedJobRetryTimeCycle');
expect(newSubProcessExtElems[0].body).to.equal('foobar');
}));
@ -298,10 +296,11 @@ describe('util/ModelCloneHelper', function() {
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', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ]
});
@ -310,7 +309,7 @@ describe('util/ModelCloneHelper', function() {
'bpmn:eventDefinitions'
]);
var extElems = clonedElement.extensionElements.values;
var extElems = clonedElement.eventDefinitions[0].extensionElements.values;
// then
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 msgEvtDef = moddle.create('bpmn:MessageEventDefinition');
var msgEvtDef = moddle.create('bpmn:MessageEventDefinition', {
extensionElements: extensionElements
});
var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ]
});
@ -339,7 +339,7 @@ describe('util/ModelCloneHelper', function() {
'bpmn:eventDefinitions'
]);
var extElems = clonedElement.extensionElements.values;
var extElems = clonedElement.eventDefinitions[0].extensionElements.values;
// then
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 msgEvtDef = moddle.create('bpmn:MessageEventDefinition');
var msgEvtDef = moddle.create('bpmn:MessageEventDefinition', {
extensionElements: extensionElements
});
var msgIntermThrowEvt = moddle.create('bpmn:IntermediateThrowEvent', {
extensionElements: extensionElements,
eventDefinitions: [ msgEvtDef ]
});
@ -367,10 +368,8 @@ describe('util/ModelCloneHelper', function() {
'bpmn:extensionElements'
]);
var extElems = clonedElement.extensionElements;
// 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) {
var newElement = context.newElement,
refTopLevelProperty = context.refTopLevelProperty,
propDescriptor = context.propertyDescriptor;
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)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
if (isAllowed('camunda:Field', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
});
return this.canCloneProperty(newElement, refTopLevelProperty, propDescriptor);
}, this);
}
CamundaModdleExtension.$inject = [ 'eventBus' ];
module.exports = {
__init__: [ 'CamundaModdleExtension' ],
CamundaModdleExtension: [ 'type', CamundaModdleExtension ]
CamundaModdleExtension.prototype.canCloneProperty = function(newElement, refTopLevelProperty, propDescriptor) {
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 ]
};