blob: 11baa4e338bab4c607d037c7ac545c5ccea94d7b [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2005 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.eclipse.gmf.runtime.emf.commands.core.command;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.emf.commands.core.internal.MSLCommandsDebugOptions;
import org.eclipse.gmf.runtime.emf.commands.core.internal.MSLCommandsPlugin;
import org.eclipse.gmf.runtime.emf.commands.core.internal.MSLCommandsStatusCodes;
import org.eclipse.gmf.runtime.emf.commands.core.internal.l10n.EMFCommandsCoreMessages;
import org.eclipse.gmf.runtime.emf.core.edit.MRunnable;
import org.eclipse.gmf.runtime.emf.core.edit.MUndoInterval;
import org.eclipse.gmf.runtime.emf.core.exceptions.MSLActionAbandonedException;
import org.eclipse.gmf.runtime.emf.core.util.OperationUtil;
/**
* A composite model command, meaning a model command that is composed of other
* commands. All composite model commands have an associated undo interval,
* through which they can be undone or redone.
*
* @author khussey
*
*/
public class CompositeModelCommand
extends CompositeCommand
implements IUndoIntervalCommand {
/**
* The undo interval title for this composite model command.
*
*/
private final String undoIntervalTitle;
/**
* The undo interval for this composite model command.
*
*/
private MUndoInterval undoInterval = null;
/**
* The result of executing/undoing/redoing this composite model command.
*
*/
private CommandResult result = null;
/**
* Constructs a new composite model command with the specified label and
* model operation context, and a default undo interval title and key.
*
* @param label
* The label for the new composite model command.
*
*/
public CompositeModelCommand(String label) {
this(label, label);
}
/**
* Constructs a new composite model command with the specified label, model
* operation context, undo interval title, and undo interval key.
*
* @param label
* The label for the new composite model command.
* @param undoIntervalTitle
* The undo interval title for the new composite model command.
*
*/
public CompositeModelCommand(String label, String undoIntervalTitle) {
this(label, undoIntervalTitle, new ArrayList());
}
/**
* Constructs a new composite model command with the specified label, model
* operation context, and a list of commands
*
* @param label
* The label for the new composite model command.
* @param commands
* The initial list of commands
*
*/
public CompositeModelCommand(String label, List commands) {
this(label, label, commands);
}
/**
* Constructs a new composite model command with the specified label, model
* operation context, undo interval title, undo interval key and list of
* commands.
*
* @param label
* The label for the new composite model command.
* @param undoIntervalTitle
* The undo interval title for the new composite model command.
* @param commands
* The initial list of commands
*
*/
public CompositeModelCommand(String label, String undoIntervalTitle,
List commands) {
super(label, commands);
this.undoIntervalTitle = undoIntervalTitle;
}
/**
* Retrieves the value of the <code>undoIntervalTitle</code> instance
* variable.
*
* @return The value of the <code>undoIntervalTitle</code> instance
* variable.
*
*/
protected final String getUndoIntervalTitle() {
return undoIntervalTitle;
}
/**
* Retrieves the value of the <code>undoInterval</code> instance variable.
*
* @return The value of the <code>undoInterval</code> instance variable.
* @see org.eclipse.gmf.runtime.emf.commands.core.command.IUndoIntervalCommand#getUndoInterval()
*/
public final MUndoInterval getUndoInterval() {
return undoInterval;
}
/**
* Sets the <code>undoInterval</code> instance variable to the specified
* value.
*
* @param undoInterval
* The new value for the <code>undoInterval</code> instance
* variable.
*
*/
protected final void setUndoInterval(MUndoInterval undoInterval) {
this.undoInterval = undoInterval;
}
/**
* Retrieves the value of the <code>result</code> instance variable.
*
* @return The value of the <code>result</code> instance variable.
*
* @see org.eclipse.gmf.runtime.common.core.command.ICommand#getCommandResult()
*
*/
public CommandResult getCommandResult() {
return result;
}
/**
* Sets the <code>result</code> instance variable to the specified value.
*
* @param result
* The new value for the <code>result</code> instance variable.
*
*/
protected final void setResult(CommandResult result) {
this.result = result;
}
/**
* Creates a new command result with an OK status.
*
* @return A new command result with an OK status.
*
*/
protected CommandResult newOKCommandResult() {
return new CommandResult(new Status(IStatus.OK, MSLCommandsPlugin
.getPluginId(), MSLCommandsStatusCodes.OK, EMPTY_STRING, null));
}
/**
* Handles the specified exception.
*
* @param exception
* The exception to be handled.
*
*/
protected void handle(Exception exception) {
Trace.catching(MSLCommandsPlugin.getDefault(),
MSLCommandsDebugOptions.EXCEPTIONS_CATCHING, getClass(),
"handle", exception); //$NON-NLS-1$
setResult(new CommandResult(new Status(IStatus.ERROR, MSLCommandsPlugin
.getPluginId(), MSLCommandsStatusCodes.MODEL_COMMAND_FAILURE, String
.valueOf(exception.getMessage()), exception)));
Log.log(MSLCommandsPlugin.getDefault(), getCommandResult().getStatus());
}
/**
* Executes this composite model command in an undo interval.
* <P>
* The <code>progressMonitor</code> must not have already begun a task.
* This method will begin a task on the progress monitor with the amount of
* work equal to the number of subcommands in this composite. Each
* subcommand will be executed with a {@link SubProgressMonitor}that will
* consume one unit of work in <code>progressMonitor</code>.
*
* @see org.eclipse.gmf.runtime.common.core.command.ICommand#execute(IProgressMonitor)
*/
public final void execute(final IProgressMonitor progressMonitor) {
if (!getValidator().okToEdit(this)) {
setResult(newCancelledCommandResult());
} else {
final IProgressMonitor monitor = (progressMonitor == null) ? new NullProgressMonitor()
: progressMonitor;
final List[] executedCommands = new List[1];
if (OperationUtil.isUncheckedInProgress()) {
executedCommands[0] = executeCommands(monitor);
} else {
final MRunnable runnable = new MRunnable() {
public Object run() {
try {
executedCommands[0] = executeCommands(monitor);
} finally {
if (isCanceled()) {
// Abandon the write action so that
// validation is not done and events are
// not fired.
this.abandon();
}
}
return null;
}
};
setUndoInterval(OperationUtil.runInUndoInterval(
getUndoIntervalTitle(), new Runnable() {
public void run() {
try {
OperationUtil.runAsWrite(runnable);
} catch (MSLActionAbandonedException e) {
// set the result status to ERROR
handleActionAbandoned(e);
}
}
}));
}
if (isCanceled()) {
undoCancelledCommands(executedCommands[0]);
}
}
}
/**
* Handles the action abandoned exception by setting an error result.
*/
protected void handleActionAbandoned(MSLActionAbandonedException e) {
// No need to consider a null undo interval here because
// such actions are never validated, so the abandon action event should
// never be fired.
setResult(new CommandResult(new Status(IStatus.ERROR, getPluginId(),
MSLCommandsStatusCodes.VALIDATION_FAILURE,
EMFCommandsCoreMessages.AbstractModelCommand__ERROR__abandonedActionErrorMessage,
null)));
Trace.trace(MSLCommandsPlugin.getDefault(), MSLCommandsDebugOptions.MODEL_OPERATIONS, "MSLActionAbandonedException"); //$NON-NLS-1$
}
/**
* @param monitor
*/
private List executeCommands(final IProgressMonitor monitor) {
setCanceled(false);
int totalWork = getCommands().size();
List executedCommands = new ArrayList(totalWork);
monitor.beginTask(getLabel(), totalWork);
for (Iterator i = getCommands().iterator(); i.hasNext();) {
SubProgressMonitor subprogressMonitor = new SubProgressMonitor(
monitor, 1);
subprogressMonitor.beginTask(getLabel(), 1);
ICommand nextCommand = (ICommand) i.next();
nextCommand.execute(subprogressMonitor);
executedCommands.add(nextCommand);
subprogressMonitor.done();
if (monitor.isCanceled()) {
monitor.done();
setResult(newCancelledCommandResult());
setCanceled(true);
return executedCommands;
}
}
setResult(CompositeModelCommand.super.getCommandResult());
monitor.done();
return executedCommands;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.gmf.runtime.common.core.internal.command.CompositeCommand#undoCancelledCommands(java.util.List)
*/
protected void undoCancelledCommands(List executedCommands) {
if (getUndoInterval() != null) {
getUndoInterval().undo();
}
}
/**
* Redoes this composite model command via its client.
*
* @see org.eclipse.gmf.runtime.common.core.command.ICommand#redo()
*
*/
public void redo() {
if (!getValidator().okToEdit(this)) {
setCanceled(true);
return;
}
if (getUndoInterval() != null) {
try {
getUndoInterval().redo();
setResult(newOKCommandResult());
Trace.trace(MSLCommandsPlugin.getDefault(),
MSLCommandsDebugOptions.MODEL_OPERATIONS,
"Composite Model Command - Redo"); //$NON-NLS-1$
} catch (Exception e) {
handle(e);
}
}
}
/**
* Undoes this composite model command via its client.
*
* @see org.eclipse.gmf.runtime.common.core.command.ICommand#undo()
*
*/
public void undo() {
if (!getValidator().okToEdit(this)) {
setCanceled(true);
return;
}
if (getUndoInterval() != null) {
try {
getUndoInterval().undo();
setResult(newOKCommandResult());
Trace.trace(MSLCommandsPlugin.getDefault(),
MSLCommandsDebugOptions.MODEL_OPERATIONS,
"Composite Model Command - Undo"); //$NON-NLS-1$
} catch (Exception e) {
handle(e);
}
}
}
public ICommand unwrap() {
switch (getCommands().size()) {
case 0:
return UnexecutableCommand.INSTANCE;
case 1:
ICommand command = (ICommand) getCommands().get(0);
if (command instanceof CompositeModelCommand || command instanceof AbstractModelCommand) {
return command;
}
}
return this;
}
}