/******************************************************************************
* 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
}