/****************************************************************************** * Copyright (c) 2005, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are 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: * IBM Corporation - initial API and implementation ****************************************************************************/ package org.yakindu.sct.ui.editor.clipboardsupport; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.emf.clipboard.core.ClipboardSupportUtil; import org.eclipse.gmf.runtime.emf.clipboard.core.CopyOperation; import org.eclipse.gmf.runtime.emf.clipboard.core.OverrideCopyOperation; import org.eclipse.gmf.runtime.emf.clipboard.core.OverridePasteChildOperation; import org.eclipse.gmf.runtime.emf.clipboard.core.PasteAction; import org.eclipse.gmf.runtime.emf.clipboard.core.PasteChildOperation; import org.eclipse.gmf.runtime.emf.clipboard.core.PasteOption; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.gmf.runtime.emf.core.clipboard.AbstractClipboardSupport; import org.eclipse.gmf.runtime.emf.type.core.commands.DestroyElementCommand; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.yakindu.sct.model.sgraph.Region; import org.yakindu.sct.model.sgraph.State; import org.yakindu.sct.model.sgraph.Transition; import org.yakindu.sct.model.sgraph.Vertex; /** * This is a copy of * {@link org.eclipse.gmf.runtime.notation.providers.internal.copypaste.NotationClipboardOperationHelper} * We had to override some static methods (getSemanticPasteTarget(...)) in this * class to support copy and paste of container elements and their contained * elements. The original paste command has only supported correct pasting on * the diagram element. * * Because this is done in a static method we couldn't inherit from the original * NotationClipboardOperationHelper. * * We made also adjustments here to support copying of a state's sub diagram. * * @author muehlbrandt */ @SuppressWarnings("all") public class NotationClipboardOperationHelper extends AbstractClipboardSupport { public void destroy(EObject eObject) { DestroyElementCommand.destroy(eObject); } /** * By default, there are no collisions in pasting. * * @return the {@link PasteAction#ADD}action, always */ public PasteAction getPasteCollisionAction(EClass eClass) { return PasteAction.ADD; } /** * By default, the following paste options are supported: * <ul> * <li>{@link PasteOption#NORMAL}: always</li> * <li>{@link PasteOption#PARENT}: never</li> * <li>{@link PasteOption#DISTANT}: if and only only if the * <code>eStructuralFeature</code> is a * {@link org.eclipse.gmf.runtime.notation.View}'s reference to its semantic * {@linkplain org.eclipse.gmf.runtime.notation.View#getElement() element}</li> * </ul> */ public boolean hasPasteOption(EObject contextEObject, EStructuralFeature eStructuralFeature, PasteOption pasteOption) { if (pasteOption.equals(PasteOption.NORMAL)) { return true; } else if (pasteOption.equals(PasteOption.PARENT)) { // disable the copy-parent functionality completely. return false; } else if (pasteOption.equals(PasteOption.DISTANT)) { if (eStructuralFeature == null) { return false; } else { return NotationPackage.eINSTANCE.getView_Element().equals( eStructuralFeature); } } else { return false; } } /** * By default, transient and derived references are never copied, and * containment references and the * {@linkplain org.eclipse.gmf.runtime.notation.View#getElement() element} * reference always are copied. * * For diagrams, the * {@linkplain org.eclipse.gmf.runtime.notation.View#getElement() element} * reference is not copied when the value is a state which indicates the * copying of a subdiagram, while the state will be copied with the * corresponding node. */ public boolean isCopyAlways(EObject context, EReference eReference, Object value) { if ((eReference.isTransient()) || (eReference.isDerived())) { return false; } else if (eReference.equals(NotationPackage.eINSTANCE .getView_Element()) && !isSubdiagram(context, value)) { return true; } else { return eReference.isContainment(); } } /** * By default, don't provide any child paste override behaviour. */ public boolean shouldOverrideChildPasteOperation(EObject parentElement, EObject childEObject) { return (childEObject.eClass().getEPackage() == NotationPackage.eINSTANCE); } /** * By default, don't provide any copy override behaviour. */ public boolean shouldOverrideCopyOperation(Collection eObjects, Map hintMap) { return false; } protected boolean shouldAllowPaste( PasteChildOperation overriddenChildPasteOperation) { EObject eObject = overriddenChildPasteOperation.getEObject(); EObject parentEObject = overriddenChildPasteOperation .getParentEObject(); // RATLC01137919 removed the condition that parentEObject is a diagram // to allow paste into diagram elements if ((parentEObject instanceof View) && (eObject instanceof View)) { EObject semanticChildElement = ((View) eObject).getElement(); if (semanticChildElement == null || isSubdiagram(eObject, semanticChildElement)) { return true; } // PATCH START EObject target = getSemanticPasteTarget((View) eObject, (View) overriddenChildPasteOperation.getParentEObject()); if (target == null) { return false; } // PATCH END if (semanticChildElement.eIsProxy()) { semanticChildElement = ClipboardSupportUtil.resolve( semanticChildElement, overriddenChildPasteOperation .getParentPasteProcess() .getLoadedIDToEObjectMapCopy()); if (semanticChildElement.eIsProxy()) { semanticChildElement = EcoreUtil.resolve( semanticChildElement, getResource(parentEObject)); } } EPackage semanticChildEpackage = semanticChildElement.eClass() .getEPackage(); EPackage parentRootContainerEpackage = EcoreUtil .getRootContainer(parentEObject).eClass().getEPackage(); EPackage sematicParentRootContainerEpackage = null; EObject sematicParentElement = ((View) parentEObject).getElement(); if (sematicParentElement != null) { sematicParentRootContainerEpackage = EcoreUtil .getRootContainer(sematicParentElement).eClass() .getEPackage(); } if (parentRootContainerEpackage != NotationPackage.eINSTANCE) { if (semanticChildEpackage != parentRootContainerEpackage) { return false; } } if ((sematicParentRootContainerEpackage != null) && (sematicParentRootContainerEpackage != NotationPackage.eINSTANCE)) { if (semanticChildEpackage != sematicParentRootContainerEpackage) { return false; } } return true; } return false; } private boolean isSubdiagram(EObject container, Object element) { return (container instanceof Diagram) && (element instanceof State); } /** * By default, don't provide any child paste override behaviour. * * @return <code>null</code>, always */ public OverridePasteChildOperation getOverrideChildPasteOperation( PasteChildOperation overriddenChildPasteOperation) { if (shouldAllowPaste(overriddenChildPasteOperation)) { EObject eObject = overriddenChildPasteOperation.getEObject(); if (eObject instanceof org.eclipse.gmf.runtime.notation.Node) { org.eclipse.gmf.runtime.notation.Node node = (org.eclipse.gmf.runtime.notation.Node) eObject; EObject element = node.getElement(); if ((element != null)) { return new PositionalGeneralViewPasteOperation(overriddenChildPasteOperation, true); } else { return new PositionalGeneralViewPasteOperation(overriddenChildPasteOperation, false); } } else if (eObject instanceof Edge) { return new ConnectorViewPasteOperation(overriddenChildPasteOperation); } else if (eObject instanceof Diagram) { return new DiagramPasteOperation(overriddenChildPasteOperation); } } return null; } /** * By default, don't provide any copy override behaviour. * * @return <code>null</code>, always */ public OverrideCopyOperation getOverrideCopyOperation( CopyOperation overriddenCopyOperation) { return null; } /** * By default, don't exclude any objects from the copy operation. * * @return an empty collection */ public Collection getExcludedCopyObjects(Set eObjects) { return Collections.EMPTY_SET; } /** * By default, just get the resource that contains the object. */ public XMLResource getResource(EObject eObject) { XMLResource eResource = (XMLResource) eObject.eResource(); if (eResource == null) { if (eObject instanceof View) { EObject element = ((View) eObject).getElement(); if ((element != null)) { return (XMLResource) element.eResource(); } } } return eResource; } /** * By default, we always copy all contents of an object. * * @return <code>true</code> */ public boolean shouldSaveContainmentFeature(EObject eObj) { if (EcorePackage.eINSTANCE.getEClassifiers().contains(eObj.eClass())) { return false; } try { eObj.eResource().getURIFragment(eObj); } catch (Exception ex) { return false; } return true; } /** * This is a quick fix to remove outgoing transitions of copied vertices if * they have no target. * */ //FIXME: See Ticket #292 Copy&Paste should only copy selected Transitions if valid. public void performPostPasteProcessing(Set pastedEObjects) { for (Object object : pastedEObjects) { if (object instanceof State) { final State state = (State) object; try { new AbstractTransactionalCommand( TransactionUtil.getEditingDomain(state), "Remove invalid connections", null) { @Override protected CommandResult doExecuteWithResult( IProgressMonitor monitor, IAdaptable info) throws ExecutionException { removeInvalidTransitions(state); return CommandResult.newOKCommandResult(); } private void removeInvalidTransitions(State state) { List<Transition> invalidTransitions = new LinkedList<Transition>(); for (Transition transition : state .getOutgoingTransitions()) { if (transition.getTarget() == null) { invalidTransitions.add(transition); } } state.getOutgoingTransitions().removeAll( invalidTransitions); for (Region region: state.getRegions()) { for (Vertex vertex:region.getVertices()) { if (vertex instanceof State) { removeInvalidTransitions((State) vertex); } } } } }.execute(new NullProgressMonitor(), null); } catch (ExecutionException e) { e.printStackTrace(); } } } } public static Diagram getContainingDiagram(View view) { EObject current = view; while (current != null) { if (current instanceof Diagram) { return (Diagram) current; } current = current.eContainer(); } return null; } // PATCH START /** * Customized Method to find the semantic target which should contain the * copied elements. * * @param view * @param container * @return the semantic target. */ public static EObject getSemanticPasteTarget(View view, View container) { EObject copiedSemanticObject = view.getElement(); EObject semanticTarget = container.getElement(); if (copiedSemanticObject instanceof Transition) { semanticTarget = copiedSemanticObject.eContainer(); } EList<EReference> eAllReferences = semanticTarget.eClass() .getEAllReferences(); for (EReference eReference : eAllReferences) { EClass eReferenceType = eReference.getEReferenceType(); if (eReference.isContainment() && eReferenceType.isSuperTypeOf(copiedSemanticObject .eClass())) { return semanticTarget; } } return null; } public static EObject getSemanticPasteTarget(View view) { return getSemanticPasteTarget(view, (View) view.eContainer()); } // PATCH END }