2022-10-12 14:21:19 +00:00
|
|
|
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
|
2022-11-24 19:59:09 +00:00
|
|
|
import { getDi, is } from 'bpmn-js/lib/util/ModelUtil';
|
|
|
|
import { remove as collectionRemove } from 'diagram-js/lib/util/Collections';
|
2022-10-19 20:16:54 +00:00
|
|
|
import {
|
2022-11-24 19:59:09 +00:00
|
|
|
findDataObjects,
|
|
|
|
findDataReferenceShapes,
|
|
|
|
idToHumanReadableName,
|
|
|
|
} from './DataObjectHelpers';
|
|
|
|
|
|
|
|
const HIGH_PRIORITY = 1500;
|
2022-10-12 14:21:19 +00:00
|
|
|
/**
|
|
|
|
* This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events
|
|
|
|
* from Diagram.js and updates the underlying BPMN model accordingly.
|
|
|
|
*
|
|
|
|
* This handles some special cases we want to handle for DataObjects and DataObjectReferences,
|
|
|
|
* for instance:
|
|
|
|
* 1) Use existing data objects if possible when creating a new reference (don't create new objects each time)
|
|
|
|
* 2) Don't automatically delete a data object when you delete the reference - unless all references are removed.
|
|
|
|
* 3) Update the name of the DataObjectReference to match the id of the DataObject.
|
|
|
|
* 4) Don't allow someone to move a DataObjectReference from one process to another process.
|
|
|
|
*/
|
|
|
|
export default class DataObjectInterceptor extends CommandInterceptor {
|
2022-11-24 19:59:09 +00:00
|
|
|
constructor(eventBus, bpmnFactory, commandStack) {
|
2022-10-12 14:21:19 +00:00
|
|
|
super(eventBus);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For DataObjectReferences only ...
|
|
|
|
* Prevent this from calling the CreateDataObjectBehavior in BPMN-js, as it will
|
|
|
|
* attempt to crete a dataObject immediately. We can't create the dataObject until
|
|
|
|
* we know where it is placed - as we want to reuse data objects of the parent when
|
|
|
|
* possible */
|
2022-11-24 19:59:09 +00:00
|
|
|
this.preExecute(['shape.create'], HIGH_PRIORITY, function (event) {
|
|
|
|
const { context } = event;
|
|
|
|
const { shape } = context;
|
2022-10-12 14:21:19 +00:00
|
|
|
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
|
|
|
event.stopPropagation();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Don't just create a new data object, use the first existing one if it already exists
|
|
|
|
*/
|
2022-11-24 19:59:09 +00:00
|
|
|
this.executed(['shape.create'], HIGH_PRIORITY, function (event) {
|
|
|
|
const { context } = event;
|
|
|
|
const { shape } = context;
|
2022-10-12 14:21:19 +00:00
|
|
|
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
2022-11-24 19:59:09 +00:00
|
|
|
const process = shape.parent.businessObject;
|
|
|
|
const existingDataObjects = findDataObjects(process);
|
2022-10-12 14:21:19 +00:00
|
|
|
let dataObject;
|
|
|
|
if (existingDataObjects.length > 0) {
|
|
|
|
dataObject = existingDataObjects[0];
|
|
|
|
} else {
|
|
|
|
dataObject = bpmnFactory.create('bpmn:DataObject');
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the reference to the DataObject
|
|
|
|
shape.businessObject.dataObjectRef = dataObject;
|
|
|
|
}
|
|
|
|
});
|
2022-10-19 20:16:54 +00:00
|
|
|
|
2022-11-24 19:59:09 +00:00
|
|
|
/**
|
|
|
|
* In order for the label to display correctly, we need to update it in POST step.
|
|
|
|
*/
|
|
|
|
this.postExecuted(['shape.create'], HIGH_PRIORITY, function (event) {
|
|
|
|
const { context } = event;
|
|
|
|
const { shape } = context;
|
|
|
|
// set the reference to the DataObject
|
|
|
|
// Update the name of the reference to match the data object's id.
|
|
|
|
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
|
|
|
commandStack.execute('element.updateProperties', {
|
|
|
|
element: shape,
|
|
|
|
moddleElement: shape.businessObject,
|
|
|
|
properties: {
|
|
|
|
name: idToHumanReadableName(shape.businessObject.dataObjectRef.id),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-10-19 20:16:54 +00:00
|
|
|
/**
|
|
|
|
* Don't remove the associated DataObject, unless all references to that data object
|
|
|
|
* Difficult to do given placement of this logic in the BPMN Updater, so we have
|
|
|
|
* to manually handle the removal.
|
|
|
|
*/
|
2022-11-24 19:59:09 +00:00
|
|
|
this.executed(['shape.delete'], HIGH_PRIORITY, function (event) {
|
2022-10-19 20:16:54 +00:00
|
|
|
const { context } = event;
|
|
|
|
const { shape, oldParent } = context;
|
|
|
|
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
|
|
|
|
const references = findDataReferenceShapes(
|
|
|
|
oldParent,
|
|
|
|
shape.businessObject.dataObjectRef.id
|
|
|
|
);
|
|
|
|
if (references.length === 0) {
|
|
|
|
return; // Use the default bahavior and delete the data object.
|
|
|
|
}
|
|
|
|
// Remove the business Object
|
|
|
|
let containment = '';
|
|
|
|
const { businessObject } = shape;
|
|
|
|
if (is(businessObject, 'bpmn:DataOutputAssociation')) {
|
|
|
|
containment = 'dataOutputAssociations';
|
|
|
|
}
|
|
|
|
if (is(businessObject, 'bpmn:DataInputAssociation')) {
|
|
|
|
containment = 'dataInputAssociations';
|
|
|
|
}
|
|
|
|
const children = businessObject.$parent.get(containment);
|
|
|
|
collectionRemove(children, businessObject);
|
|
|
|
|
|
|
|
// Remove the visible element.
|
|
|
|
const di = getDi(shape);
|
|
|
|
const planeElements = di.$parent.get('planeElement');
|
|
|
|
collectionRemove(planeElements, di);
|
|
|
|
di.$parent = null;
|
|
|
|
|
|
|
|
// Stop the propogation.
|
|
|
|
event.stopPropagation();
|
|
|
|
}
|
|
|
|
});
|
2022-10-12 14:21:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-24 19:59:09 +00:00
|
|
|
DataObjectInterceptor.$inject = ['eventBus', 'bpmnFactory', 'commandStack'];
|