blob: 63a46217f2e7c2cca88b7fa7bd7f6aff909fac99 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006 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.ui.action;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.ICompositeOperation;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
/**
* Command used to allow subclasses of the {@link AbstractModelActionDelegate}
* to read and write to the model from their #doRun implementation.
* <P>
* This is a kind of {@link ICompositeOperation} that opens itself on the
* operation history. It runs a {@link Runnable} supplied at the time of
* instantiation and any operations executed through the history in that
* runnable are considered to be part of the composite, and can be undone and
* redone in a single step. This allows file modification validation to be done
* on each command executed by a subclass of {@link AbstractModelActionDelegate}.
*
* @author ldamus
*/
abstract class WriteCommand extends AbstractTransactionalCommand implements
ICompositeOperation {
private final CompositeTransactionalCommand compositeDelegate;
private final Runnable runnable;
private final IOperationHistory history;
/**
* Initializes me.
*
* @param domain
* my editing domain
* @param label
* my user-readable label
* @param history
* the operation history
* @param runnable
* the runnable to run when I execute
*/
public WriteCommand(TransactionalEditingDomain domain, String label,
IOperationHistory history, Runnable runnable) {
super(domain, label, null);
this.compositeDelegate = new CompositeTransactionalCommand(domain,
label);
this.runnable = runnable;
this.history = history;
}
/**
* Opens a composite operation on the operation history and runs the
* runnable.
*/
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
if (runnable != null) {
history.openOperation(this, IOperationHistory.EXECUTE);
try {
runnable.run();
history.closeOperation(getStatus().isOK(), false,
IOperationHistory.EXECUTE);
} catch (RuntimeException e) {
history.closeOperation(false, false, IOperationHistory.EXECUTE);
throw e;
}
}
// have to compute the result because we didn't actually execute the
// compositeDelegate
return computeResult();
}
/**
* Redoes the commands that were accumulated when the runnable was run.
*/
protected CommandResult doRedoWithResult(IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
compositeDelegate.redo(progressMonitor, info);
return compositeDelegate.getCommandResult();
}
/**
* Undoes the commands that were accumulated when the runnable was run.
*/
protected CommandResult doUndoWithResult(IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
compositeDelegate.undo(progressMonitor, info);
return compositeDelegate.getCommandResult();
}
/**
* Adds to my {@link CompositeTransactionalCommand} delegate.
*/
public void add(IUndoableOperation operation) {
compositeDelegate.add(operation);
refreshContexts();
}
/**
* Removes from my {@link CompositeTransactionalCommand} delegate.
*/
public void remove(IUndoableOperation operation) {
compositeDelegate.remove(operation);
refreshContexts();
}
private void refreshContexts() {
IUndoContext[] currentContexts = getContexts();
for (int i = 0; i < currentContexts.length; i++) {
if (!compositeDelegate.hasContext(currentContexts[i])) {
removeContext(currentContexts[i]);
}
}
IUndoContext[] newContexts = compositeDelegate.getContexts();
for (int i = 0; i < newContexts.length; i++) {
if (!hasContext(newContexts[i])) {
addContext(newContexts[i]);
}
}
}
/**
* Gets the affected files from my {@link CompositeTransactionalCommand}
* delegate.
*/
public List getAffectedFiles() {
return compositeDelegate.getAffectedFiles();
}
/**
* Computes a command result based on the contents of my
* {@link CompositeTransactionalCommand} delegate.
*
* @return the command result
*/
private CommandResult computeResult() {
if (compositeDelegate.size() == 0) {
return null;
} else if (compositeDelegate.size() == 1) {
IUndoableOperation operation = (IUndoableOperation) compositeDelegate
.iterator().next();
if (operation instanceof ICommand) {
return ((ICommand) operation).getCommandResult();
}
return new CommandResult(Status.OK_STATUS);
}
IStatus worst = Status.OK_STATUS;
List statuses = new ArrayList();
List returnValues = new ArrayList();
for (Iterator i = compositeDelegate.iterator(); i.hasNext();) {
IUndoableOperation operation = (IUndoableOperation) i.next();
if (operation instanceof ICommand) {
ICommand command = (ICommand) operation;
CommandResult result = command.getCommandResult();
if (result != null) {
IStatus nextStatus = result.getStatus();
if (nextStatus.getSeverity() > worst.getSeverity()) {
worst = nextStatus;
}
statuses.add(nextStatus);
Object nextValue = result.getReturnValue();
if (nextValue != null) {
if (getClass().isInstance(command)) {
// unwrap the values from other composites
if (nextValue != null
&& nextValue instanceof Collection) {
returnValues.addAll((Collection) nextValue);
} else {
returnValues.add(nextValue);
}
} else {
returnValues.add(nextValue);
}
}
}
}
}
IStatus status = new MultiStatus(worst.getPlugin(), worst.getCode(),
(IStatus[]) statuses.toArray(new IStatus[statuses.size()]),
worst.getMessage(), null);
return new CommandResult(status, returnValues);
}
public abstract IStatus getStatus();
}