/**
 * File:    ReconnectEdgeCommand.java
 * Created: 28.02.2006
 *
/*******************************************************************************
 * 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
 *******************************************************************************/

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

import org.eclipse.emf.common.command.AbstractCommand;


/**
 * Command to reconnect an edge to a new source or target.
 * 
 * @param <M>
 *            The type of the edge.
 * @param <S>
 *            The type of the source.
 * @param <T>
 *            The type of the target.
 * 
 * @version $Id: ReconnectEdgeCommand.java,v 1.3 2009-11-26 15:16:21 chsaad Exp $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming distributed
 *         Systems Lab, University of Augsburg, Germany, www.ds-lab.org
 */
public abstract class ReconnectEdgeCommand<M, S, T>
		extends AbstractCommand
{

	/**
	 * The edge to reconnect.
	 */
	private M edge;

	/**
	 * The original source endpoint.
	 */
	private S oldSource;

	/**
	 * The original target endpoint.
	 */
	private T oldTarget;

	/**
	 * The new source endpoint.
	 */
	private S newSource;

	/**
	 * The new target endpoint.
	 */
	private T newTarget;

	/**
	 * The new object;
	 */
	private Object newObject;


	/**
	 * @param label
	 *            The Command's label.
	 * @param edge
	 *            The edge to reconnect.
	 */
	public ReconnectEdgeCommand(String label, M edge)
	{
		super(label);
		this.edge = edge;
	}


	/**
	 * @param label
	 *            The Command's label.
	 * @param createRequest
	 *            A request to create a new edge.
	 */
	public ReconnectEdgeCommand(String label, Object newObject, int x)
	{
		super(label);

		this.newObject = newObject;
	}


	/**
	 * @return Returns the edge.
	 */
	public M getEdge()
	{
		if (edge == null && newObject != null)
		{
			edge = (M) newObject;
		}

		return edge;
	}


	/**
	 * @return Returns the oldSource.
	 */
	public S getOldSource()
	{
		return oldSource;
	}


	/**
	 * @param oldSource
	 *            The oldSource to set.
	 */
	protected void setOldSource(S oldSource)
	{
		this.oldSource = oldSource;
	}


	/**
	 * Initialises the value of the source.
	 * 
	 * @param source
	 *            The current source endpoint.
	 */
	public void initSource(S source)
	{
		oldSource = source;
		newSource = source;
	}


	/**
	 * @return Returns the oldTarget.
	 */
	public T getOldTarget()
	{
		return oldTarget;
	}


	/**
	 * @param oldTarget
	 *            The oldTarget to set.
	 */
	protected void setOldTarget(T oldTarget)
	{
		this.oldTarget = oldTarget;
	}


	/**
	 * Initialises the value of the target.
	 * 
	 * @param target
	 *            The current target endpoint.
	 */
	public void initTarget(T target)
	{
		oldTarget = target;
		newTarget = target;
	}


	/**
	 * @return Returns the newSource.
	 */
	public S getNewSource()
	{
		return newSource;
	}


	/**
	 * @param newSource
	 *            The newSource to set.
	 */
	public void setNewSource(S newSource)
	{
		this.newSource = newSource;
	}


	/**
	 * @return Returns the newTarget.
	 */
	public T getNewTarget()
	{
		return newTarget;
	}


	/**
	 * @param newTarget
	 *            The newTarget to set.
	 */
	public void setNewTarget(T newTarget)
	{
		this.newTarget = newTarget;
	}


	/**
	 * Sets the source endpoint for the edge.
	 * 
	 * @param edge
	 *            The edge.
	 * @param source
	 *            The source endpoint to set.
	 */
	protected abstract void setSourceEndpoint(M edge, S source);


	/**
	 * Sets the target endpoint for the edge.
	 * 
	 * @param edge
	 *            The edge.
	 * @param target
	 *            The target endpoint to set.
	 */
	protected abstract void setTargetEndpoint(M edge, T target);


	/**
	 * Checks if the source connection is valid.
	 * 
	 * This checks only if conditions for the source connected are satisfied. The target
	 * connection may not be set.
	 * 
	 * @return <code>true</code>, if the source connection can be created, otherwise
	 *         <code>false</code>.
	 */
	public boolean checkSourceConnection()
	{
		return true;
	}


	/**
	 * Checks if the target connection is valid.
	 * 
	 * This checks only if conditions for the target connected are satisfied. The target
	 * connection may not be set.
	 * 
	 * @return <code>true</code>, if the target connection can be created, otherwise
	 *         <code>false</code>.
	 */
	public boolean checkTargetConnection()
	{
		return true;
	}


	/**
	 * Checks if the connection is valid.
	 * 
	 * Both source and target connection are set. Disallows self connection.
	 * 
	 * @return <code>true</code>, if the command can be executed, otherwise
	 *         <code>false</code>.
	 */
	public boolean checkConnection()
	{
		if (newTarget != null && newTarget == newSource)
		{
			return false;
		}

		return true;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	@Override
	public boolean canExecute()
	{
		if (edge == null && newObject == null)
		{
			return false;
		}

		// always allow existing connection
		if (newSource == oldSource && newTarget == oldTarget)
		{
			return true;
		}

		return checkSourceConnection() && checkTargetConnection() && checkConnection();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	public void execute()
	{
		if (newSource != oldSource)
		{
			setSourceEndpoint(getEdge(), getNewSource());
		}

		if (newTarget != oldTarget)
		{
			setTargetEndpoint(getEdge(), getNewTarget());
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	@Override
	public void undo()
	{
		if (newSource != oldSource)
		{
			setSourceEndpoint(getEdge(), getOldSource());
		}

		if (newTarget != oldTarget)
		{
			setTargetEndpoint(getEdge(), getOldTarget());
		}
	}
}
