| /******************************************************************************* |
| * Copyright (c) 2011, 2012, 2013 Red Hat, Inc. |
| * All rights reserved. |
| * This program is made available under the terms of the |
| * Eclipse Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Red Hat, Inc. - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.bpmn2.modeler.core.features; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map.Entry; |
| |
| import org.eclipse.bpmn2.Activity; |
| import org.eclipse.bpmn2.Association; |
| import org.eclipse.bpmn2.BaseElement; |
| import org.eclipse.bpmn2.BoundaryEvent; |
| import org.eclipse.bpmn2.Bpmn2Factory; |
| import org.eclipse.bpmn2.Collaboration; |
| import org.eclipse.bpmn2.ConversationLink; |
| import org.eclipse.bpmn2.Definitions; |
| import org.eclipse.bpmn2.FlowElement; |
| import org.eclipse.bpmn2.FlowElementsContainer; |
| import org.eclipse.bpmn2.FlowNode; |
| import org.eclipse.bpmn2.InteractionNode; |
| import org.eclipse.bpmn2.Lane; |
| import org.eclipse.bpmn2.LaneSet; |
| import org.eclipse.bpmn2.MessageFlow; |
| import org.eclipse.bpmn2.Participant; |
| import org.eclipse.bpmn2.Process; |
| import org.eclipse.bpmn2.SequenceFlow; |
| import org.eclipse.bpmn2.di.BPMNDiagram; |
| import org.eclipse.bpmn2.di.BPMNEdge; |
| import org.eclipse.bpmn2.di.BPMNShape; |
| import org.eclipse.bpmn2.modeler.core.di.DIUtils; |
| import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerFactory; |
| import org.eclipse.bpmn2.modeler.core.utils.AnchorUtil; |
| import org.eclipse.bpmn2.modeler.core.utils.BusinessObjectUtil; |
| import org.eclipse.bpmn2.modeler.core.utils.FeatureSupport; |
| import org.eclipse.bpmn2.modeler.core.utils.GraphicsUtil; |
| import org.eclipse.bpmn2.modeler.core.utils.ModelUtil; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EcoreUtil.Copier; |
| import org.eclipse.graphiti.datatypes.IDimension; |
| import org.eclipse.graphiti.datatypes.ILocation; |
| import org.eclipse.graphiti.features.IFeatureProvider; |
| import org.eclipse.graphiti.features.IUpdateFeature; |
| import org.eclipse.graphiti.features.context.IPasteContext; |
| import org.eclipse.graphiti.features.context.impl.AddConnectionContext; |
| import org.eclipse.graphiti.features.context.impl.AddContext; |
| import org.eclipse.graphiti.features.context.impl.AreaContext; |
| import org.eclipse.graphiti.features.context.impl.UpdateContext; |
| import org.eclipse.graphiti.mm.algorithms.styles.Point; |
| import org.eclipse.graphiti.mm.pictograms.Anchor; |
| import org.eclipse.graphiti.mm.pictograms.Connection; |
| import org.eclipse.graphiti.mm.pictograms.ContainerShape; |
| import org.eclipse.graphiti.mm.pictograms.Diagram; |
| import org.eclipse.graphiti.mm.pictograms.FreeFormConnection; |
| import org.eclipse.graphiti.mm.pictograms.PictogramElement; |
| import org.eclipse.graphiti.mm.pictograms.Shape; |
| import org.eclipse.graphiti.services.Graphiti; |
| import org.eclipse.graphiti.ui.editor.DiagramEditor; |
| import org.eclipse.graphiti.ui.features.AbstractPasteFeature; |
| |
| /** |
| * Default Graphiti {@code PasteFeature} class for Shapes. |
| * <p> |
| */ |
| public class DefaultPasteBPMNElementFeature extends AbstractPasteFeature { |
| |
| /** The EMF Resource. */ |
| protected Resource resource; |
| |
| /** The BPMN2 Definitions object - the root element of the document. */ |
| protected Definitions definitions; |
| |
| /** |
| * Maps the ID strings of the original BPMN2 elements to their |
| * corresponding newly constructed copies. |
| */ |
| protected Hashtable<String, String> idMap; |
| |
| /** The shape map. */ |
| protected HashMap<ContainerShape, ContainerShape> shapeMap; |
| |
| /** The connection map. */ |
| protected HashMap<Connection, Connection> connectionMap; |
| |
| /** The x reference. */ |
| protected int xReference; |
| |
| /** The y reference. */ |
| protected int yReference; |
| |
| /** The diagram. */ |
| protected Diagram diagram; |
| |
| /** |
| * Instantiates a new default {@code PasteFeature). |
| * |
| * @param fp the Feature Provider |
| */ |
| public DefaultPasteBPMNElementFeature(IFeatureProvider fp) { |
| super(fp); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.graphiti.features.IPasteFeature#canPaste(org.eclipse.graphiti.features.context.IPasteContext) |
| */ |
| @Override |
| public boolean canPaste(IPasteContext context) { |
| // target must be a FlowElementsContainer (Process, etc.) |
| ContainerShape targetContainerShape = getTargetContainerShape(context); |
| if (targetContainerShape==null) |
| return false; |
| BaseElement targetContainerObject = getContainerObject(targetContainerShape); |
| if (targetContainerObject==null) |
| return false; |
| |
| Object[] pasteObjects; |
| if (context.getProperty(GraphitiConstants.COPY_FROM_CONTEXT) != null) { |
| // Get objects to paste from the context. This can be used to test if |
| // objects can be pasted before they have been copied to the clipboard. |
| pasteObjects = context.getPictogramElements(); |
| } |
| else { |
| // can paste, if all objects on the clipboard are PictogramElements |
| pasteObjects = getFromClipboard(); |
| } |
| |
| if (pasteObjects == null || pasteObjects.length == 0) { |
| return false; |
| } |
| int count = 0; |
| for (Object object : pasteObjects) { |
| if (!(object instanceof PictogramElement)) { |
| continue; |
| } |
| PictogramElement pe = (PictogramElement) object; |
| BaseElement be = BusinessObjectUtil.getFirstBaseElement(pe); |
| if (!(be instanceof FlowElement) && !(be instanceof Lane) && !(be instanceof Participant)) { |
| continue; |
| } |
| // can't paste Boundary Events directly - these are "carried along" |
| // by the Activity to which they are attached. |
| if (be instanceof BoundaryEvent) { |
| continue; |
| } |
| // can't paste Label shapes |
| if (pe instanceof Shape && FeatureSupport.isLabelShape((Shape)pe)) { |
| continue; |
| } |
| // Participants can only be pasted into into a Collaboration |
| if (be instanceof Participant && !(targetContainerObject instanceof Collaboration)) { |
| continue; |
| } |
| ++count; |
| } |
| if (count==0) |
| return false; |
| |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.graphiti.features.IPasteFeature#paste(org.eclipse.graphiti.features.context.IPasteContext) |
| */ |
| @Override |
| public void paste(IPasteContext context) { |
| ContainerShape targetContainerShape = getTargetContainerShape(context); |
| BaseElement targetContainerObject = getContainerObject(targetContainerShape); |
| |
| // save the Diagram and Resource needed for constructing the new objects |
| diagram = getFeatureProvider().getDiagramTypeProvider().getDiagram(); |
| resource = targetContainerObject.eResource(); |
| definitions = ModelUtil.getDefinitions(resource); |
| idMap = new Hashtable<String, String>(); |
| shapeMap = new HashMap<ContainerShape, ContainerShape>(); |
| connectionMap = new HashMap<Connection, Connection>(); |
| xReference = 0; |
| yReference = 0; |
| |
| int xMin = Integer.MAX_VALUE; |
| int yMin = Integer.MAX_VALUE; |
| Object[] fromClipboard = getFromClipboard(); |
| for (Object object : fromClipboard) { |
| if (object instanceof ContainerShape) { |
| ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram((ContainerShape) object); |
| if (loc.getX() < xMin) { |
| xMin = loc.getX(); |
| } |
| if (loc.getY() < yMin) { |
| yMin = loc.getY(); |
| } |
| } |
| } |
| if (xMin!=Integer.MAX_VALUE) { |
| xReference = xMin; |
| yReference = yMin; |
| } |
| |
| int x = context.getX(); |
| int y = context.getY(); |
| if (!(targetContainerShape instanceof Diagram)) { |
| ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(targetContainerShape); |
| x -= loc.getX(); |
| y -= loc.getY(); |
| } |
| |
| // First create all shapes. This creates a lookup map of old to new |
| // ContainerShape objects. |
| for (Object object : fromClipboard) { |
| if (object instanceof ContainerShape) { |
| copyShape((ContainerShape) object, targetContainerShape, x, y); |
| } |
| } |
| |
| // Handle connections now that we know all shapes have been created |
| x = context.getX(); // Connection bendpoint coordinates are always |
| // relative to diagram |
| y = context.getY(); |
| for (Object object : fromClipboard) { |
| if (object instanceof Connection) { |
| copyConnection((Connection) object, targetContainerShape, x, y); |
| } |
| } |
| |
| // handle any connections that were not created because of missing source/target |
| for (Entry<Connection, Connection> entry : connectionMap.entrySet()) { |
| if (entry.getValue()==null) { |
| copyConnection(entry.getKey(), targetContainerShape, x, y); |
| } |
| } |
| PictogramElement newPes[] = new PictogramElement[shapeMap.size()]; |
| int i = 0; |
| for (Entry<ContainerShape, ContainerShape> entry : shapeMap.entrySet()) { |
| newPes[i++] = entry.getValue(); |
| } |
| |
| getDiagramEditor().setPictogramElementsForSelection(newPes); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.graphiti.ui.features.AbstractPasteFeature#getFromClipboard() |
| */ |
| protected Object[] getFromClipboard() { |
| List<Object> allObjects = new ArrayList<Object>(); |
| Object[] objects = super.getFromClipboard(); |
| for (Object object : objects) { |
| if (object instanceof EObject && ((EObject)object).eContainer()!=null) |
| allObjects.add(object); |
| } |
| |
| List<Object> filteredObjects = new ArrayList<Object>(); |
| for (Object object : allObjects) { |
| if (object instanceof EObject && ((EObject)object).eContainer()!=null) { |
| if (object instanceof ContainerShape) { |
| filteredObjects.add(object); |
| } |
| else if (object instanceof Connection) { |
| Connection c = (Connection)object; |
| if (c.getStart()!=null && c.getEnd()!=null) { |
| if (allObjects.contains(c.getStart().getParent()) && |
| allObjects.contains(c.getEnd().getParent())) { |
| filteredObjects.add(object); |
| } |
| } |
| } |
| } |
| } |
| |
| return filteredObjects.toArray(); |
| } |
| |
| public <T extends EObject> T copyEObject(T eObject) { |
| Copier copier = new Copier() { |
| @Override |
| protected EObject createCopy(EObject eObject) { |
| EClass eClass = getTarget(eObject.eClass()); |
| if (eClass.getEPackage().getEFactoryInstance() == Bpmn2Factory.eINSTANCE) { |
| return Bpmn2ModelerFactory.createObject(resource, eClass); |
| } |
| return super.createCopy(eObject); |
| } |
| |
| }; |
| EObject result = copier.copy(eObject); |
| copier.copyReferences(); |
| |
| @SuppressWarnings("unchecked") |
| T t = (T) result; |
| |
| // don't set a name on the new object if old object didn't have one. |
| EStructuralFeature f = t.eClass().getEStructuralFeature("name"); //$NON-NLS-1$ |
| if (f!=null) { |
| String name = (String)eObject.eGet(f); |
| if (name==null || name.isEmpty()) |
| t.eSet(f, null); |
| } |
| return t; |
| } |
| |
| private BaseElement createNewObject(BaseElement oldObject, BaseElement targetContainerObject) { |
| BaseElement newObject = null; |
| try { |
| Bpmn2ModelerFactory.lock(); |
| Bpmn2ModelerFactory.setEnableModelExtensions(false); |
| newObject = copyEObject(oldObject); |
| } |
| finally { |
| Bpmn2ModelerFactory.setEnableModelExtensions(true); |
| Bpmn2ModelerFactory.unlock(); |
| } |
| |
| if (targetContainerObject instanceof Participant) { |
| // need to create a Process for target container if it doesn't have one yet |
| Participant participant = (Participant) targetContainerObject; |
| if (participant.getProcessRef()==null) { |
| Process process = Bpmn2ModelerFactory.createObject(resource, Process.class); |
| participant.setProcessRef(process); |
| } |
| targetContainerObject = participant.getProcessRef(); |
| } |
| |
| // get rid of some of the objects created by EcoreUtil.copy() as these will be |
| // constructed here because we need to create the Graphiti shapes and DI elements |
| // along with these |
| if (newObject instanceof FlowElementsContainer) { |
| // we will create our own FlowElements, thank you! |
| ((FlowElementsContainer)newObject).getFlowElements().clear(); |
| } |
| |
| if (newObject instanceof Lane) { |
| // we will construct these ourselves |
| ((Lane) newObject).getFlowNodeRefs().clear(); |
| ((Lane) newObject).setChildLaneSet(null); |
| if (targetContainerObject instanceof FlowElementsContainer) { |
| FlowElementsContainer fc = (FlowElementsContainer)targetContainerObject; |
| if (fc.getLaneSets().size()!=0) { |
| fc.getLaneSets().get(0).getLanes().add((Lane)newObject); |
| } |
| else { |
| LaneSet ls = Bpmn2ModelerFactory.createObject(resource, LaneSet.class); |
| fc.getLaneSets().add(ls); |
| ls.getLanes().add((Lane)newObject); |
| } |
| } |
| else if (targetContainerObject instanceof Lane) { |
| Lane ln = (Lane)targetContainerObject; |
| if (ln.getChildLaneSet()==null) { |
| LaneSet ls = Bpmn2ModelerFactory.createObject(resource, LaneSet.class); |
| ln.setChildLaneSet(ls); |
| } |
| ln.getChildLaneSet().getLanes().add((Lane)newObject); |
| } |
| } |
| else if (newObject instanceof FlowElement) { |
| if (targetContainerObject instanceof Lane) { |
| Lane ln = (Lane)targetContainerObject; |
| targetContainerObject = getFlowElementsContainer(ln); |
| // newObject could be either a Shape (FlowNode) or a Connection; |
| // only add FlowNodes to the Lane's FlowNodeRefs list. |
| if (newObject instanceof FlowNode) |
| ln.getFlowNodeRefs().add((FlowNode)newObject); |
| } |
| if (targetContainerObject instanceof FlowElementsContainer) { |
| ((FlowElementsContainer)targetContainerObject).getFlowElements().add((FlowElement) newObject); |
| } |
| } |
| else if (newObject instanceof Participant) { |
| Participant participant = (Participant)newObject; |
| if (((Participant) newObject).getProcessRef()!=null) { |
| // need to create a new Process for this thing |
| Process process = Bpmn2ModelerFactory.createObject(resource, Process.class); |
| participant.setProcessRef(process); |
| } |
| if (targetContainerObject instanceof Collaboration) { |
| Collaboration collab = (Collaboration)targetContainerObject; |
| collab.getParticipants().add((Participant)newObject); |
| } |
| } |
| |
| // Ensure IDs are unique |
| setId(newObject); |
| |
| TreeIterator<EObject> iter = newObject.eAllContents(); |
| while (iter.hasNext()) { |
| EObject newChild = iter.next(); |
| setId(newChild); |
| } |
| |
| for (EReference ref : newObject.eClass().getEAllReferences()) { |
| if (!ref.isContainment()) { |
| Object oldValue = oldObject.eGet(ref); |
| // TODO: do we need this? |
| // this mess also duplicates "incoming" and "outgoing" (for SequenceFlows) |
| // which are already being handled in copyConnection()... |
| // if (oldValue instanceof EObjectEList) { |
| // EObjectEList oldList = (EObjectEList)oldObject.eGet(ref); |
| // EObjectEList newList = (EObjectEList)newObject.eGet(ref); |
| // for (Object oldRefObject : oldList) { |
| // if (oldRefObject instanceof EObject) { |
| // String oldId = getId((EObject)oldRefObject); |
| // if (oldId!=null) { |
| // String newId = idMap.get(oldId); |
| // EObject newRefObject = findObjectById(newId); |
| // newList.add(newRefObject); |
| // } |
| // } |
| // } |
| // } |
| // else |
| if (oldValue instanceof EObject){ |
| EObject oldRefObject = (EObject)oldValue; |
| String oldId = getId(oldRefObject); |
| if (oldId!=null) { |
| String newId = idMap.get(oldId); |
| if (newId!=null) { |
| EObject newRefObject = findObjectById(newId); |
| newObject.eSet(ref, newRefObject); |
| } |
| else if (newObject.eGet(ref) != null){ |
| EObject newRefObject = (EObject) newObject.eGet(ref); |
| newId = getId(newRefObject); |
| if (newId!=null) |
| idMap.put(oldId, newId); |
| } |
| } |
| } |
| } |
| } |
| return newObject; |
| } |
| |
| private String getId(EObject newObject) { |
| EStructuralFeature feature = newObject.eClass().getEStructuralFeature("id"); //$NON-NLS-1$ |
| if (feature != null) { |
| return (String) newObject.eGet(feature); |
| } |
| return null; |
| } |
| |
| private String setId(EObject newObject) { |
| String newId = null; |
| String oldId = null; |
| EStructuralFeature feature = newObject.eClass().getEStructuralFeature("id"); //$NON-NLS-1$ |
| if (feature != null) { |
| oldId = (String) newObject.eGet(feature); |
| if (idMap.contains(oldId)) { |
| newId = idMap.get(oldId); |
| newObject.eSet(feature, newId); |
| } |
| else { |
| newObject.eUnset(feature); |
| newId = ModelUtil.setID(newObject); |
| idMap.put(oldId, newId); |
| } |
| } |
| return oldId; |
| } |
| |
| private boolean wasCopied(EObject object) { |
| String id = getId(object); |
| if (id!=null) { |
| return idMap.containsValue(id); |
| } |
| return false; |
| } |
| |
| private EObject findObjectById(String id) { |
| TreeIterator<EObject> iter = definitions.eAllContents(); |
| while (iter.hasNext()) { |
| EObject o = iter.next(); |
| EStructuralFeature feature = o.eClass().getEStructuralFeature("id"); //$NON-NLS-1$ |
| if (feature != null) { |
| String thisId = (String) o.eGet(feature); |
| if (thisId != null && !thisId.isEmpty() && thisId.equals(id)) |
| return o; |
| } |
| } |
| return null; |
| } |
| |
| private ContainerShape findShape(EObject object) { |
| List<PictogramElement> pes = Graphiti.getLinkService().getPictogramElements(diagram, object); |
| for (PictogramElement pe : pes) { |
| if (pe instanceof ContainerShape) |
| return (ContainerShape) pe; |
| } |
| return null; |
| } |
| |
| private Connection findConnection(EObject object) { |
| List<PictogramElement> pes = Graphiti.getLinkService().getPictogramElements(diagram, object); |
| for (PictogramElement pe : pes) { |
| if (pe instanceof Connection) |
| return (Connection) pe; |
| } |
| return null; |
| } |
| |
| private BaseElement copyShape(ContainerShape oldShape, ContainerShape targetContainerShape, int x, int y) { |
| if (shapeMap.get(oldShape)!=null) |
| return null; |
| |
| BaseElement targetContainerObject = getContainerObject(targetContainerShape); |
| BaseElement oldObject = BusinessObjectUtil.getFirstBaseElement(oldShape); |
| BaseElement newObject = createNewObject(oldObject, targetContainerObject); |
| |
| AddContext ac = new AddContext(new AreaContext(), newObject); |
| ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(oldShape); |
| IDimension size = GraphicsUtil.calculateSize(oldShape); |
| // The default Add BPMN Shape feature will position the new shape so its |
| // center is at the target location; for copy/paste we want to use the |
| // top-left corner instead so that copied connection bendpoints (if any) |
| // line up properly. |
| int deltaX = 0; |
| int deltaY = 0; |
| if (oldObject instanceof FlowNode) { |
| deltaX = loc.getX() - xReference + size.getWidth() / 2; |
| deltaY = loc.getY() - yReference + size.getHeight() / 2; |
| } |
| ac.setLocation(x + deltaX, y + deltaY); |
| ac.setSize(size.getWidth(), size.getHeight()); |
| ac.setTargetContainer(targetContainerShape); |
| |
| BPMNShape oldBpmnShape = null; |
| if (oldObject instanceof BaseElement) { |
| BPMNDiagram bpmnDiagram = DIUtils.findBPMNDiagram(oldShape); |
| oldBpmnShape = DIUtils.findBPMNShape(bpmnDiagram, (BaseElement)oldObject); |
| ac.putProperty(GraphitiConstants.COPIED_BPMN_DI_ELEMENT, oldBpmnShape); |
| } |
| ac.putProperty(GraphitiConstants.COPIED_BPMN_OBJECT, oldObject); |
| |
| |
| ContainerShape newShape = (ContainerShape) getFeatureProvider().addIfPossible(ac); |
| |
| shapeMap.put(oldShape, newShape); |
| |
| if (oldObject instanceof Participant) { |
| // copy the contained Process elements |
| oldObject = ((Participant)oldObject).getProcessRef(); |
| } |
| |
| // create shapes and connections for children if this is a FlowElementsContainer |
| if (oldObject instanceof FlowElementsContainer) { |
| List<ContainerShape> childShapes = new ArrayList<ContainerShape>(); |
| List<Connection> childConnections = new ArrayList<Connection>(); |
| TreeIterator<EObject> iter = oldObject.eAllContents(); |
| while (iter.hasNext()) { |
| // look up the old child object that corresponds to the new child object |
| EObject oldChildObject = iter.next(); |
| if (oldChildObject instanceof BoundaryEvent) { |
| // Defer Boundary Event creation until we're sure that the |
| // new attachedToRef task is actually created. |
| continue; |
| } |
| if (wasCopied(oldChildObject)) { |
| // stop infinite recursion: this would happen if a FlowElementsContainer |
| //was copied into itself. |
| continue; |
| } |
| |
| // if the old child has a Graphiti ContainerShape, duplicate it. |
| ContainerShape oldChildShape = findShape(oldChildObject); |
| if (oldChildShape != null) { |
| childShapes.add(oldChildShape); |
| } |
| Connection oldChildConnection = findConnection(oldChildObject); |
| if (oldChildConnection != null) { |
| childConnections.add(oldChildConnection); |
| } |
| } |
| |
| for (ContainerShape oldChildShape : childShapes) { |
| copyShape(oldChildShape, newShape, 0, 0); |
| } |
| |
| for (Connection oldChildConnection : childConnections) { |
| copyConnection(oldChildConnection, newShape, x, y); |
| } |
| } |
| else if (oldObject instanceof Lane) { |
| List<PictogramElement> shapes = new ArrayList<PictogramElement>(); |
| Lane oldLane = (Lane)oldObject; |
| if (oldLane.getChildLaneSet()!=null) { |
| for (Lane oldChildLaneObject : oldLane.getChildLaneSet().getLanes()) { |
| ContainerShape oldChildLaneShape = findShape(oldChildLaneObject); |
| if (oldChildLaneShape != null) { |
| copyShape(oldChildLaneShape, newShape, 0, 0); |
| } |
| } |
| } |
| for (FlowNode oldChildObject : oldLane.getFlowNodeRefs()) { |
| ContainerShape oldChildShape = findShape(oldChildObject); |
| if (oldChildShape != null) { |
| copyShape(oldChildShape, newShape, 0, 0); |
| shapes.add(oldChildShape); |
| } |
| } |
| List<Connection> connections = DefaultCopyBPMNElementFeature.findAllConnections(shapes); |
| for (Connection oldChildConnection : connections) { |
| copyConnection(oldChildConnection, newShape, x, y); |
| } |
| } |
| |
| // also copy the BPMNShape properties |
| if (oldBpmnShape!=null) { |
| BPMNShape newBpmnShape = DIUtils.findBPMNShape((BaseElement)newObject); |
| newBpmnShape.setIsExpanded(oldBpmnShape.isIsExpanded()); |
| newBpmnShape.setIsHorizontal(oldBpmnShape.isIsHorizontal()); |
| newBpmnShape.setIsMarkerVisible(oldBpmnShape.isIsMarkerVisible()); |
| newBpmnShape.setIsMessageVisible(oldBpmnShape.isIsMessageVisible()); |
| newBpmnShape.setParticipantBandKind(oldBpmnShape.getParticipantBandKind()); |
| } |
| |
| UpdateContext uc = new UpdateContext(newShape); |
| IUpdateFeature uf = getFeatureProvider().getUpdateFeature(uc); |
| // force an update to cause the newly created ContainerShape to be rendered properly |
| uc.putProperty(GraphitiConstants.FORCE_UPDATE_ALL, Boolean.TRUE); |
| uf.update(uc); |
| |
| if (newObject instanceof Activity) { |
| // copy the Activity's Boundary Events if it has any |
| TreeIterator<EObject> i = definitions.eAllContents(); |
| while (i.hasNext()) { |
| EObject o = i.next(); |
| if (o instanceof BoundaryEvent) { |
| BoundaryEvent oldBeObject = (BoundaryEvent)o; |
| if (oldBeObject.getAttachedToRef() == oldObject) { |
| // here's one... |
| ContainerShape oldBeShape = findShape(oldBeObject); |
| copyShape(oldBeShape, targetContainerShape, x, y); |
| } |
| } |
| } |
| } |
| |
| return newObject; |
| } |
| |
| private BaseElement copyConnection(Connection oldConnection, ContainerShape targetContainerShape, int x, int y) { |
| if (connectionMap.get(oldConnection)!=null) |
| return null; |
| |
| BaseElement targetContainerObject = getContainerObject(targetContainerShape); |
| BaseElement oldObject = BusinessObjectUtil.getFirstBaseElement(oldConnection); |
| BaseElement newObject = createNewObject(oldObject, targetContainerObject); |
| |
| Anchor oldStart = oldConnection.getStart(); |
| Anchor oldEnd = oldConnection.getEnd(); |
| ContainerShape newSource = shapeMap.get(oldStart.getParent()); |
| ContainerShape newTarget = shapeMap.get(oldEnd.getParent()); |
| if (newSource==null || newTarget==null) { |
| // source or target does not exist yet - handle this connection later |
| connectionMap.put(oldConnection, null); |
| return null; |
| } |
| |
| Anchor newStart; |
| Anchor newEnd; |
| ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(oldStart); |
| newStart = AnchorUtil.createAnchor(newSource, loc.getX(), loc.getY()); |
| |
| loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(oldEnd); |
| newEnd = AnchorUtil.createAnchor(newTarget, loc.getX(), loc.getY()); |
| |
| BaseElement newSourceObject = BusinessObjectUtil.getFirstBaseElement(newSource); |
| BaseElement newTargetObject = BusinessObjectUtil.getFirstBaseElement(newTarget); |
| if (newObject instanceof SequenceFlow) { |
| ((SequenceFlow) newObject).setSourceRef((FlowNode) newSourceObject); |
| ((SequenceFlow) newObject).setTargetRef((FlowNode) newTargetObject); |
| } |
| else if (newObject instanceof Association) { |
| ((Association) newObject).setSourceRef((FlowNode) newSourceObject); |
| ((Association) newObject).setTargetRef((FlowNode) newTargetObject); |
| } |
| else if (newObject instanceof MessageFlow) { |
| ((MessageFlow) newObject).setSourceRef((InteractionNode) newSourceObject); |
| ((MessageFlow) newObject).setTargetRef((InteractionNode) newTargetObject); |
| } |
| else if (newObject instanceof ConversationLink) { |
| ((ConversationLink) newObject).setSourceRef((InteractionNode) newSourceObject); |
| ((ConversationLink) newObject).setTargetRef((InteractionNode) newTargetObject); |
| } |
| AddConnectionContext acc = new AddConnectionContext(newStart, newEnd); |
| acc.setNewObject(newObject); |
| |
| Connection newConnection = (Connection) getFeatureProvider().addIfPossible(acc); |
| connectionMap.put(oldConnection, newConnection); |
| |
| if (oldConnection instanceof FreeFormConnection && newConnection instanceof FreeFormConnection) { |
| for (Point p : ((FreeFormConnection) oldConnection).getBendpoints()) { |
| int deltaX = p.getX() - xReference; |
| int deltaY = p.getY() - yReference; |
| Point newPoint = GraphicsUtil.createPoint(x + deltaX, y + deltaY); |
| ((FreeFormConnection) newConnection).getBendpoints().add(newPoint); |
| } |
| } |
| |
| // also copy the BPMNEdge properties |
| if (oldObject instanceof BaseElement) { |
| BPMNDiagram bpmnDiagram = DIUtils.findBPMNDiagram(oldConnection); |
| BPMNEdge oldBpmnEdge = DIUtils.findBPMNEdge(bpmnDiagram, (BaseElement)oldObject); |
| if (oldBpmnEdge!=null) { |
| bpmnDiagram = DIUtils.findBPMNDiagram(newConnection); |
| BPMNEdge newBpmnEdge = DIUtils.findBPMNEdge(bpmnDiagram, (BaseElement)newObject); |
| newBpmnEdge.setMessageVisibleKind(oldBpmnEdge.getMessageVisibleKind()); |
| } |
| } |
| |
| FeatureSupport.updateConnection(getFeatureProvider(), newConnection); |
| |
| return newObject; |
| } |
| |
| private ContainerShape getTargetContainerShape(IPasteContext context) { |
| Diagram diagram = getFeatureProvider().getDiagramTypeProvider().getDiagram(); |
| |
| Point p = GraphicsUtil.createPoint(context.getX(), context.getY()); |
| Shape s = GraphicsUtil.findShapeAt(diagram, p, new GraphicsUtil.IShapeFilter() { |
| @Override |
| public boolean matches(Shape shape) { |
| if (shape instanceof ContainerShape) { |
| BaseElement be = getContainerObject((ContainerShape) shape); |
| return be instanceof FlowElementsContainer || be instanceof Participant; |
| } |
| return false; |
| } |
| }); |
| if (s!=null) |
| return (ContainerShape) s; |
| return diagram; |
| } |
| |
| private BaseElement getContainerObject(ContainerShape targetContainerShape) { |
| EObject bo = BusinessObjectUtil.getBusinessObjectForPictogramElement(targetContainerShape); |
| if (bo instanceof BPMNDiagram) { |
| bo = ((BPMNDiagram) bo).getPlane().getBpmnElement(); |
| } |
| if (bo instanceof Participant) { |
| if (!FeatureSupport.isChoreographyParticipantBand(targetContainerShape)) |
| return (Participant) bo; |
| bo = ((Participant) bo).getProcessRef(); |
| } |
| if (bo instanceof FlowElementsContainer || bo instanceof Lane || bo instanceof Collaboration) |
| return (BaseElement) bo; |
| return null; |
| } |
| |
| private FlowElementsContainer getFlowElementsContainer(Lane lane) { |
| EObject container = lane.eContainer(); |
| while (!(container instanceof FlowElementsContainer) && container!=null) |
| container = container.eContainer(); |
| return (FlowElementsContainer)container; |
| } |
| |
| protected DiagramEditor getDiagramEditor() { |
| return (DiagramEditor)getFeatureProvider().getDiagramTypeProvider().getDiagramBehavior().getDiagramContainer(); |
| } |
| } |