/**
 * File:    CustomCutToClipboardCommand.java
 * Created: 14.06.2008
 *
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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:
 *    Markus Bauer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg 
 *    	- rewrote from scratch
 *******************************************************************************/

package org.eclipse.jwt.meta.commands.clipboard;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.CutToClipboardCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jwt.meta.PluginProperties;
import org.eclipse.jwt.meta.commands.helper.CommandHelper;
import org.eclipse.jwt.meta.commands.interfaces.IInterruptibleCommand;
import org.eclipse.jwt.meta.model.core.Package;
import org.eclipse.jwt.meta.model.processes.ActivityEdge;

/**
 * This is a custom CutToClipboard command, that: 1. does not place the original
 * elements into the clipboard but copies 2. also copies the edges that exist
 * between these elemets into the clipboard 3. aborts, if still referenced
 * elements are affected (RefElems, Activities)
 * 
 * @version $Id: CustomCutToClipboardCommand.java,v 1.2.2.1 2009/01/20 15:25:38
 *          chsaad Exp $
 * @author Christian Saad (csaad@gmx.net), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org
 */
public class JWTCutToClipboardCommand extends CutToClipboardCommand implements
		IInterruptibleCommand
{

	/**
	 * The constructor.
	 * 
	 * @param domain
	 * @param command
	 */
	public JWTCutToClipboardCommand(EditingDomain domain, Command command)
	{
		super(domain, command);
	}


	/**
	 * Returns a list of all remove commands contained in a CompoundCommand
	 * (recursively) including the command itself.
	 * 
	 * @param command
	 * @return
	 */
	protected Set getAllRemoveCommandsRecursively(Command command)
	{
		Set commands = new HashSet();

		if (command instanceof CompoundCommand)
		{
			for (Iterator iteratorC = ((CompoundCommand) command).getCommandList()
					.iterator(); iteratorC.hasNext();)
			{
				commands.addAll(getAllRemoveCommandsRecursively((Command) iteratorC
						.next()));
			}
		}
		else if (command instanceof RemoveCommand)
		{
			commands.add(command);
		}

		return commands;
	}


	/**
	 * This is executed just before the command is executed. If this function
	 * returns false, the command will not be executed. It checks whether the
	 * elements that should be deleted have active references. If not, it can be
	 * executed. If yes the execution is aborted.
	 * 
	 * @return true if the command should be executed
	 */
	public boolean checkIfCommandCanBeExecuted()
	{
		// for each element that should be deleted, collect all
		// ReferenceableElements and
		// Activities of this element (recursively if necessary).
		Set relevantElements = new HashSet();
		Set removeCommands = getAllRemoveCommandsRecursively(command);
		for (Iterator iterator = removeCommands.iterator(); iterator.hasNext();)
		{
			RemoveCommand removeCommand = (RemoveCommand) iterator.next();
			for (Iterator iterator2 = removeCommand.getCollection().iterator(); iterator2
					.hasNext();)
			{
				EObject object = (EObject) iterator2.next();
				relevantElements.addAll(CommandHelper.collectRelevantElements(object));
			}

		}

		// collect all elements which are still referenced somewhere in the
		// model starting
		// with the root model object
		Package rootPackage = null;
		if (relevantElements.size() > 0)
		{
			rootPackage = (Package) CommandHelper.getRoot((EObject) relevantElements
					.iterator().next());
		}
		else
		{
			return true;
		}

		Set referencedElements = CommandHelper.collectReferencedElements(
				relevantElements, rootPackage, false);

		// if no elements are referenced -> command can be executed
		if (referencedElements.size() == 0)
		{
			return true;
		}
		else
		{
			// collect names of elements which are still referenced
			String refNameString = "\n\n" //$NON-NLS-1$
					+ CommandHelper.collectNames(referencedElements);

			// show message
			MessageDialog.openInformation(null,
					PluginProperties.editor_StillReferenced_title, PluginProperties.bind(
							PluginProperties.editor_StillReferenced_message,
							refNameString));
			return false;
		}
	}


	/**
	 * This method searches the command for all (recursively) contained remove
	 * commands and adds all objects which should be deleted to a collection.
	 * Then, all edges are added to this collection which exist between objects
	 * in the collection.
	 * 
	 * @return A list of objects to be removed, including the edges between the
	 *         objects.
	 */
	private Set getAffectedObjectsForClipboard()
	{
		// search recursively for remove commands contained in the given command
		Set removeCommands = getAllRemoveCommandsRecursively(command);

		// all objects to be deleted except ReferenceEdges and ActivityEdges
		Set affectedObjects = new HashSet();
		for (Iterator iterator = removeCommands.iterator(); iterator.hasNext();)
		{
			RemoveCommand removeCommand = (RemoveCommand) iterator.next();

			for (Iterator iterator2 = removeCommand.getCollection().iterator(); iterator2
					.hasNext();)
			{
				EObject object = (EObject) iterator2.next();

				if (!(object instanceof ActivityEdge))
				{
					affectedObjects.add(object);
				}
			}
		}

		// add all edges which exist between the affected objects
		affectedObjects.addAll(CommandHelper.getAffectedEdges(affectedObjects));

		return affectedObjects;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.CutToClipboardCommand#execute()
	 */
	@Override
	public void execute()
	{
		// get all objects to be removed, including all edges which exist
		// between them
		Set affectedObjects = getAffectedObjectsForClipboard();

		// save old clipboard and place a copy of the affected objects in the
		// clipboard
		// (also unset refElement features of actions, if the corresponding
		// ReferenceEdge/Reference is not present in the clipboard)
		if (domain != null)
		{
			oldClipboard = domain.getClipboard();

			Collection copy = EcoreUtil.copyAll(affectedObjects);
			domain.setClipboard(copy);
		}

		// execute remove command (from CommandWrapper: super.super.execute)
		if (command != null)
		{
			command.execute();
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.command.CutToClipboardCommand#redo()
	 */
	@Override
	public void redo()
	{
		// get all objects to be removed, including all edges which exist
		// between them
		Set affectedObjects = getAffectedObjectsForClipboard();

		// save old clipboard and place a copy of the affected objects in the
		// clipboard
		// (also unset refElement features of actions, if the corresponding
		// ReferenceEdge/Reference is not present in the clipboard)
		if (domain != null)
		{
			oldClipboard = domain.getClipboard();

			Collection copy = EcoreUtil.copyAll(affectedObjects);
			domain.setClipboard(copy);
		}

		// execute remove command (from CommandWrapper: super.super.execute)
		if (command != null)
		{
			command.redo();
		}
	}

}