import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; import { getDi, is } from 'bpmn-js/lib/util/ModelUtil'; import { remove as collectionRemove } from 'diagram-js/lib/util/Collections'; import { findDataObjects, findDataReferenceShapes, idToHumanReadableName, } from './DataObjectHelpers'; const HIGH_PRIORITY = 1500; /** * 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 { constructor(eventBus, bpmnFactory, commandStack) { 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 */ this.preExecute(['shape.create'], HIGH_PRIORITY, function (event) { const { context } = event; const { shape } = context; 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 */ this.executed(['shape.create'], HIGH_PRIORITY, function (event) { const { context } = event; const { shape } = context; if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') { const process = shape.parent.businessObject; const existingDataObjects = findDataObjects(process); 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; } }); /** * 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), }, }); } }); /** * 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. */ this.executed(['shape.delete'], HIGH_PRIORITY, function (event) { 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(); } }); } } DataObjectInterceptor.$inject = ['eventBus', 'bpmnFactory', 'commandStack'];