blob: 69be548ed2c8f83c36634f999c2005be89861be8 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 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.eclipse.gmf.runtime.emf.commands.core.command;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.workspace.AbstractEMFOperation;
import org.eclipse.emf.workspace.CompositeEMFOperation;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.core.command.ICompositeCommand;
import org.eclipse.gmf.runtime.common.core.internal.command.ICommandWithSettableResult;
import org.eclipse.gmf.runtime.common.core.util.StringStatics;
/**
* An undoable operation that is composed of child {@link IUndoableOperation}s
* that are expected to modify EMF model resources.
* <p>
* The operation provides a list of {@link IFile}s that are expected to be
* modified when the operation is executed, undone or redone. An
* {@link IOperationApprover} is registered with the
* {@link OperationHistoryFactory#getOperationHistory()} to validate the
* modification to these resources.
* <P>
* This class is meant to be instantiated by clients.
*
* @author ldamus
*/
public class CompositeTransactionalCommand
extends CompositeEMFOperation
implements ICompositeCommand, ICommandWithSettableResult {
private CommandResult commandResult;
/**
* Initializes me with the editing domain in which I am making model changes
* and a label.
*
* @param domain
* my editing domain
* @param label
* my user-readable label, should never be <code>null</code>.
*/
public CompositeTransactionalCommand(TransactionalEditingDomain domain,
String label) {
super(domain, (label == null) ? StringStatics.BLANK : label);
}
/**
* Initializes me with the editing domain, a label, and transaction options.
*
* @param domain
* my editing domain
* @param label
* my user-readable label, should never be <code>null</code>.
* @param options
* for the transaction in which I execute myself, or
* <code>null</code> for the default options
*/
public CompositeTransactionalCommand(TransactionalEditingDomain domain,
String label, Map options) {
super(domain, (label == null) ? StringStatics.BLANK : label, options);
}
/**
* Initializes me with the editing domain, a label, and child operations.
*
* @param domain
* my editing domain
* @param label
* my user-readable label, should never be <code>null</code>.
* @param children
* a list of operations to compose
*/
public CompositeTransactionalCommand(TransactionalEditingDomain domain,
String label, List children) {
super(domain, (label == null) ? StringStatics.BLANK : label, children);
}
/**
* Initializes me with the editing domain, a label, and child operations,
* and transaction options.
*
* @param domain
* my editing domain
* @param label
* my user-readable label, should never be <code>null</code>.
* @param children
* a list of operations to compose
* @param options
* for the transaction in which I execute myself, or
* <code>null</code> for the default options
*/
public CompositeTransactionalCommand(TransactionalEditingDomain domain,
String label, List children, Map options) {
super(domain, (label == null) ? StringStatics.BLANK : label, children, options);
}
/**
* Returns the {@link IFile}s for resources that may be modified when the
* operation is executed, undone or redone.
*/
public List getAffectedFiles() {
HashSet result = new HashSet();
for (Iterator i = iterator(); i.hasNext();) {
IUndoableOperation nextOperation = (IUndoableOperation) i.next();
if (nextOperation instanceof ICommand) {
List nextAffected = ((ICommand) nextOperation)
.getAffectedFiles();
if (nextAffected != null) {
result.addAll(nextAffected);
}
}
}
return new ArrayList(result);
}
public CommandResult getCommandResult() {
if (commandResult == null) {
List<IStatus> statusList = new ArrayList<IStatus>(size());
for (Iterator<?> i = iterator(); i.hasNext();) {
IUndoableOperation operation = (IUndoableOperation) i.next();
if (operation instanceof ICommand) {
ICommand command = (ICommand) operation;
CommandResult result = command.getCommandResult();
if (result != null) {
statusList.add(result.getStatus());
}
}
}
// Don't set the command explicitly since the intermediate command could
// have children added later.
return new CommandResult(super.aggregateStatuses(statusList),
getReturnValues());
}
return commandResult;
}
/**
* Sets the command result.
*
* @param result
* the new result for this command.
*/
protected void setResult(CommandResult result) {
this.commandResult = result;
}
/**
* Returns a list containing all of the return values from
* <code>ICommand</code> children.
*/
protected List getReturnValues() {
List returnValues = new ArrayList();
for (Iterator i = iterator(); i.hasNext();) {
IUndoableOperation operation = (IUndoableOperation) i.next();
if (operation instanceof ICommand) {
ICommand command = (ICommand) operation;
CommandResult result = command.getCommandResult();
if (result != null) {
Object returnValue = result.getReturnValue();
if (returnValue != null) {
if (getClass().isInstance(command)) {
// unwrap the values from other composites
if (returnValue != null
&& returnValue instanceof Collection) {
returnValues.addAll((Collection) returnValue);
} else {
returnValues.add(returnValue);
}
} else {
returnValues.add(returnValue);
}
}
}
}
}
return returnValues;
}
/**
* Overrides the superclass implementation to set the command result.
*/
protected IStatus aggregateStatuses(List statuses) {
IStatus aggregate = super.aggregateStatuses(statuses);
setResult(new CommandResult(aggregate, getReturnValues()));
return aggregate;
}
// Documentation copied from the interface
public final ICommand compose(IUndoableOperation operation) {
if (operation != null) {
add(operation);
}
return this;
}
/**
* Returns the simplest form of this command that is equivalent. This is
* useful for removing unnecessary nesting of commands.
* <P>
* If the composite has a single command, it returns the reduction of that
* single command. Otherwise, it returns itself.
*
* @return the simplest form of this command that is equivalent
*/
public ICommand reduce() {
switch (size()) {
case 0:
return this;
case 1:
IUndoableOperation child = (IUndoableOperation) iterator()
.next();
if (child instanceof ICommand &&
child instanceof AbstractEMFOperation) {
// return the single command if is a kind of EMF operation;
// otherwise this composite will be returned to preserve the
// EMF transaction behaviour.
return ((ICommand) child).reduce();
}
default:
if (!isTransactionNestingEnabled()) {
List children = getChildren();
IUndoableOperation[] opChildren = (IUndoableOperation[]) children
.toArray(new IUndoableOperation[children.size()]);
children.clear();
for (int i = 0; i < opChildren.length; ++i) {
doReduce(opChildren[i], children);
}
}
}
return this;
}
private void doReduce(IUndoableOperation operation, List children) {
if (operation instanceof CompositeEMFOperation) {
for (Iterator i = ((CompositeEMFOperation) operation).iterator(); i.hasNext();) {
doReduce((IUndoableOperation) i.next(), children);
}
} else {
children.add(operation);
}
}
/**
* Answers whether or not this composite operation has children.
*
* @return <code>true</code> if the operation does not have children,
* <code>false</code> otherwise.
*/
public final boolean isEmpty() {
return size() < 1;
}
/**
* I can execute if I am not empty and all of my children can execute.
*/
public boolean canExecute() {
return !isEmpty() && super.canExecute();
}
/**
* I can redo if I am not empty and all my children can all be redone.
*/
public boolean canRedo() {
return !isEmpty() && super.canRedo();
}
/**
* I can undo if I am not empty and all my children can all be undone.
*/
public boolean canUndo() {
return !isEmpty() && super.canUndo();
}
/**
* Internal method to set the command result.
*
* @param result CommandResult to set
* @deprecated internal API
*/
public void internalSetResult(CommandResult result) {
this.commandResult = result;
}
}