| /** |
| * <copyright> |
| * |
| * Copyright (c) 2002-2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * |
| * </copyright> |
| * |
| * $Id: CompoundCommand.java,v 1.3 2005/06/06 05:36:11 david_williams Exp $ |
| */ |
| package org.eclipse.emf.common.command; |
| |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| import org.eclipse.wst.sse.core.internal.Logger; |
| |
| |
| /** |
| * A command that comprises a sequence of subcommands. Derived classes can |
| * control the way results are accumulated from the individual commands; the |
| * default behaviour is to return the result of the last command. |
| */ |
| public class CompoundCommand extends AbstractCommand { |
| /** |
| * The list of subcommands. |
| */ |
| protected List commandList; |
| |
| /** |
| * When {@link #resultIndex} is set to this, {@link #getResult} and |
| * {@link #getAffectedObjects} are delegated to the last command, if any, |
| * in the list. |
| */ |
| public static final int LAST_COMMAND_ALL = Integer.MIN_VALUE; |
| |
| /** |
| * When {@link #resultIndex} is set to this, {@link #getResult} and |
| * {@link #getAffectedObjects} are set to the result of merging the |
| * corresponding collection of each command in the list. |
| */ |
| public static final int MERGE_COMMAND_ALL = Integer.MIN_VALUE - 1; |
| |
| /** |
| * The index of the command whose result and affected objects are |
| * forwarded. Negative values have special meaning, as defined by the |
| * static constants. A value of -1 indicates that the last command in the |
| * list should be used. We could have more special behaviours implemented |
| * for other negative values. |
| */ |
| protected int resultIndex = MERGE_COMMAND_ALL; |
| |
| /** |
| * Creates an empty instance. |
| */ |
| public CompoundCommand() { |
| super(); |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given label. |
| * |
| * @param label |
| * the label. |
| */ |
| public CompoundCommand(String label) { |
| super(label); |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given label and description. |
| * |
| * @param label |
| * the label. |
| * @param description |
| * the description. |
| */ |
| public CompoundCommand(String label, String description) { |
| super(label, description); |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given list. |
| * |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(List commandList) { |
| super(); |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Creates instance with the given label and list. |
| * |
| * @param label |
| * the label. |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(String label, List commandList) { |
| super(label); |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Creates an instance with the given label, description, and list. |
| * |
| * @param label |
| * the label. |
| * @param description |
| * the description. |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(String label, String description, List commandList) { |
| super(label, description); |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Creates an empty instance with the given result index. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| */ |
| public CompoundCommand(int resultIndex) { |
| super(); |
| this.resultIndex = resultIndex; |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given result index and label. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| * @param label |
| * the label. |
| */ |
| public CompoundCommand(int resultIndex, String label) { |
| super(label); |
| this.resultIndex = resultIndex; |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given result index, label, and |
| * description. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| * @param label |
| * the label. |
| * @param description |
| * the description. |
| */ |
| public CompoundCommand(int resultIndex, String label, String description) { |
| super(label, description); |
| this.resultIndex = resultIndex; |
| commandList = new ArrayList(); |
| } |
| |
| /** |
| * Creates an instance with the given result index and list. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(int resultIndex, List commandList) { |
| super(); |
| this.resultIndex = resultIndex; |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Creates an instance with the given resultIndex, label, and list. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| * @param label |
| * the label. |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(int resultIndex, String label, List commandList) { |
| super(label); |
| this.resultIndex = resultIndex; |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Creates an instance with the given result index, label, description, |
| * and list. |
| * |
| * @param resultIndex |
| * the {@link #resultIndex}. |
| * @param label |
| * the label. |
| * @param description |
| * the description. |
| * @param commandList |
| * the list of commands. |
| */ |
| public CompoundCommand(int resultIndex, String label, String description, List commandList) { |
| super(label, description); |
| this.resultIndex = resultIndex; |
| this.commandList = commandList; |
| } |
| |
| /** |
| * Returns whether there are commands in the list. |
| * |
| * @return whether there are commands in the list. |
| */ |
| public boolean isEmpty() { |
| return commandList.isEmpty(); |
| } |
| |
| /** |
| * Returns an unmodifiable view of the commands in the list. |
| * |
| * @return an unmodifiable view of the commands in the list. |
| */ |
| public List getCommandList() { |
| return Collections.unmodifiableList(commandList); |
| } |
| |
| /** |
| * Returns the index of the command whose result and affected objects are |
| * forwarded. Negative values have special meaning, as defined by the |
| * static constants. |
| * |
| * @return the index of the command whose result and affected objects are |
| * forwarded. |
| * @see #LAST_COMMAND_ALL |
| * @see #MERGE_COMMAND_ALL |
| */ |
| public int getResultIndex() { |
| return resultIndex; |
| } |
| |
| /** |
| * Returns whether all the commands can execute so that |
| * {@link #isExecutable} can be cached. An empty command list causes |
| * <code>false</code> to be returned. |
| * |
| * @return whether all the commands can execute. |
| */ |
| protected boolean prepare() { |
| if (commandList.isEmpty()) { |
| return false; |
| } |
| else { |
| for (Iterator commands = commandList.listIterator(); commands.hasNext();) { |
| Command command = (Command) commands.next(); |
| if (!command.canExecute()) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| /** |
| * Calls {@link Command#execute} for each command in the list. |
| */ |
| public void execute() { |
| for (ListIterator commands = commandList.listIterator(); commands.hasNext();) { |
| try { |
| Command command = (Command) commands.next(); |
| command.execute(); |
| } |
| catch (RuntimeException exception) { |
| // Skip over the command that threw the exception. |
| // |
| commands.previous(); |
| |
| try { |
| // Iterate back over the executed commands to undo them. |
| // |
| while (commands.hasPrevious()) { |
| Command command = (Command) commands.previous(); |
| if (command.canUndo()) { |
| command.undo(); |
| } |
| else { |
| break; |
| } |
| } |
| } |
| catch (RuntimeException nestedException) { |
| Logger.logException(UnDoCommonMessages._UI_IgnoreException_exception, nestedException); |
| } |
| |
| throw exception; |
| } |
| } |
| } |
| |
| /** |
| * Returns <code>false</code> if any of the commands return |
| * <code>false</code> for {@link Command#canUndo}. |
| * |
| * @return <code>false</code> if any of the commands return |
| * <code>false</code> for <code>canUndo</code>. |
| */ |
| public boolean canUndo() { |
| for (Iterator commands = commandList.listIterator(); commands.hasNext();) { |
| Command command = (Command) commands.next(); |
| if (!command.canUndo()) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Calls {@link Command#undo} for each command in the list, in reverse |
| * order. |
| */ |
| public void undo() { |
| for (ListIterator commands = commandList.listIterator(commandList.size()); commands.hasPrevious();) { |
| try { |
| Command command = (Command) commands.previous(); |
| command.undo(); |
| } |
| catch (RuntimeException exception) { |
| // Skip over the command that threw the exception. |
| // |
| commands.next(); |
| |
| try { |
| // Iterate forward over the undone commands to redo them. |
| // |
| while (commands.hasNext()) { |
| Command command = (Command) commands.next(); |
| command.redo(); |
| } |
| } |
| catch (RuntimeException nestedException) { |
| Logger.logException(UnDoCommonMessages._UI_IgnoreException_exception, exception); |
| } |
| |
| |
| throw exception; |
| } |
| } |
| } |
| |
| /** |
| * Calls {@link Command#redo} for each command in the list. |
| */ |
| public void redo() { |
| for (ListIterator commands = commandList.listIterator(); commands.hasNext();) { |
| try { |
| Command command = (Command) commands.next(); |
| command.redo(); |
| } |
| catch (RuntimeException exception) { |
| // Skip over the command that threw the exception. |
| // |
| commands.previous(); |
| |
| try { |
| // Iterate back over the executed commands to undo them. |
| // |
| while (commands.hasPrevious()) { |
| Command command = (Command) commands.previous(); |
| command.undo(); |
| } |
| } |
| catch (RuntimeException nestedException) { |
| Logger.logException(UnDoCommonMessages._UI_IgnoreException_exception, nestedException); |
| } |
| |
| throw exception; |
| } |
| } |
| } |
| |
| /** |
| * Determines the result by composing the results of the commands in the |
| * list; this is affected by the setting of {@link #resultIndex}. |
| * |
| * @return the result. |
| */ |
| public Collection getResult() { |
| if (commandList.isEmpty()) { |
| return Collections.EMPTY_LIST; |
| } |
| else if (resultIndex == LAST_COMMAND_ALL) { |
| return ((Command) commandList.get(commandList.size() - 1)).getResult(); |
| } |
| else if (resultIndex == MERGE_COMMAND_ALL) { |
| return getMergedResultCollection(); |
| } |
| else if (resultIndex < commandList.size()) { |
| return ((Command) commandList.get(resultIndex)).getResult(); |
| } |
| else { |
| return Collections.EMPTY_LIST; |
| } |
| } |
| |
| /** |
| * Returns the merged collection of all command results. |
| * |
| * @return the merged collection of all command results. |
| */ |
| protected Collection getMergedResultCollection() { |
| Collection result = new ArrayList(); |
| |
| for (Iterator commands = commandList.iterator(); commands.hasNext();) { |
| Command command = (Command) commands.next(); |
| result.addAll(command.getResult()); |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * Determines the affected objects by composing the affected objects of |
| * the commands in the list; this is affected by the setting of |
| * {@link #resultIndex}. |
| * |
| * @return the affected objects. |
| */ |
| public Collection getAffectedObjects() { |
| if (commandList.isEmpty()) { |
| return Collections.EMPTY_LIST; |
| } |
| else if (resultIndex == LAST_COMMAND_ALL) { |
| return ((Command) commandList.get(commandList.size() - 1)).getAffectedObjects(); |
| } |
| else if (resultIndex == MERGE_COMMAND_ALL) { |
| return getMergedAffectedObjectsCollection(); |
| } |
| else if (resultIndex < commandList.size()) { |
| return ((Command) commandList.get(resultIndex)).getAffectedObjects(); |
| } |
| else { |
| return Collections.EMPTY_LIST; |
| } |
| } |
| |
| /** |
| * Returns the merged collection of all command affected objects. |
| * |
| * @return the merged collection of all command affected objects. |
| */ |
| protected Collection getMergedAffectedObjectsCollection() { |
| Collection result = new ArrayList(); |
| |
| for (Iterator commands = commandList.iterator(); commands.hasNext();) { |
| Command command = (Command) commands.next(); |
| result.addAll(command.getAffectedObjects()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Determines the label by composing the labels of the commands in the |
| * list; this is affected by the setting of {@link #resultIndex}. |
| * |
| * @return the label. |
| */ |
| public String getLabel() { |
| if (label != null) { |
| return label; |
| } |
| else if (commandList.isEmpty()) { |
| return UnDoCommonMessages._UI_CompoundCommand_label; |
| } |
| else if (resultIndex == LAST_COMMAND_ALL || resultIndex == MERGE_COMMAND_ALL) { |
| return ((Command) commandList.get(commandList.size() - 1)).getLabel(); |
| } |
| else if (resultIndex < commandList.size()) { |
| return ((Command) commandList.get(resultIndex)).getLabel(); |
| } |
| else { |
| return UnDoCommonMessages._UI_CompoundCommand_label; |
| } |
| } |
| |
| /** |
| * Determines the description by composing the descriptions of the |
| * commands in the list; this is affected by the setting of |
| * {@link #resultIndex}. |
| * |
| * @return the description. |
| */ |
| public String getDescription() { |
| if (description != null) { |
| return description; |
| } |
| else if (commandList.isEmpty()) { |
| return UnDoCommonMessages._UI_CompoundCommand_description; |
| } |
| else if (resultIndex == LAST_COMMAND_ALL || resultIndex == MERGE_COMMAND_ALL) { |
| return ((Command) commandList.get(commandList.size() - 1)).getDescription(); |
| } |
| else if (resultIndex < commandList.size()) { |
| return ((Command) commandList.get(resultIndex)).getDescription(); |
| } |
| else { |
| return UnDoCommonMessages._UI_CompoundCommand_description; |
| } |
| } |
| |
| /** |
| * Adds a command to this compound command's list of commands. |
| * |
| * @param command |
| * the command to append. |
| */ |
| public void append(Command command) { |
| if (command != null) { |
| commandList.add(command); |
| } |
| } |
| |
| /** |
| * Checks if the command can execute; if so, it is executed, appended to |
| * the list, and true is returned, if not, it is just disposed and false |
| * is returned. A typical use for this is to execute commands created |
| * during the execution of another command, e.g., |
| * |
| * <pre> |
| * class MyCommand extends CommandBase { |
| * protected Command subcommand; |
| * |
| * //... |
| * |
| * public void execute() |
| * { |
| * // ... |
| * Compound subcommands = new CompoundCommand(); |
| * subcommands.appendAndExecute(new AddCommand(...)); |
| * if (condition) subcommands.appendAndExecute(new AddCommand(...)); |
| * subcommand = subcommands.unwrap(); |
| * } public void undo() { |
| * // ... |
| * subcommand.undo(); |
| * } |
| * |
| * public void redo() { |
| * // ... |
| * subcommand.redo(); |
| * } |
| * |
| * public void dispose() { |
| * // ... |
| * if (subcommand != null) { |
| * subcommand.dispose(); |
| * } |
| * } |
| * } |
| * |
| * </pre> |
| * |
| * Another use is in an execute override of compound command itself: |
| * |
| * <pre> |
| * class MyCommand extends CompoundCommand { |
| * public void execute() |
| * { |
| * // ... |
| * appendAndExecute(new AddCommand(...)); |
| * if (condition) appendAndExecute(new AddCommand(...)); |
| * }} |
| * |
| * </pre> |
| * |
| * Note that appending commands will modify what getResult and |
| * getAffectedObjects return, so you may want to set the resultIndex flag. |
| * |
| * @param command |
| * the command. |
| * @return whether the command was successfully executed and appended. |
| */ |
| public boolean appendAndExecute(Command command) { |
| if (command != null) { |
| if (!isPrepared) { |
| if (commandList.isEmpty()) { |
| isPrepared = true; |
| isExecutable = true; |
| } |
| else { |
| isExecutable = prepare(); |
| isPrepared = true; |
| if (isExecutable) { |
| execute(); |
| } |
| } |
| } |
| |
| if (command.canExecute()) { |
| try { |
| command.execute(); |
| commandList.add(command); |
| return true; |
| } |
| catch (RuntimeException exception) { |
| Logger.logException(UnDoCommonMessages._UI_IgnoreException_exception, exception); |
| } |
| } |
| |
| command.dispose(); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Adds a command to this compound command's the list of commands and |
| * returns <code>true</code>, if |
| * <code>command.{@link org.eclipse.emf.common.command.Command#canExecute() canExecute()}</code> |
| * returns true; otherwise, it simply calls |
| * <code>command.{@link org.eclipse.emf.common.command.Command#dispose() dispose()}</code> |
| * and returns <code>false</code>. |
| * |
| * @param command |
| * the command. |
| * @return whether the command was executed and appended. |
| */ |
| public boolean appendIfCanExecute(Command command) { |
| if (command == null) { |
| return false; |
| } |
| else if (command.canExecute()) { |
| commandList.add(command); |
| return true; |
| } |
| else { |
| command.dispose(); |
| return false; |
| } |
| } |
| |
| /** |
| * Calls {@link Command#dispose} for each command in the list. |
| */ |
| public void dispose() { |
| for (Iterator commands = commandList.listIterator(); commands.hasNext();) { |
| Command command = (Command) commands.next(); |
| command.dispose(); |
| } |
| } |
| |
| /** |
| * Returns one of three things: |
| * {@link org.eclipse.emf.common.command.UnexecutableCommand#INSTANCE}, |
| * if there are no commands, the one command, if there is exactly one |
| * command, or <code>this</code>, if there are multiple commands; this |
| * command is {@link #dispose}d in the first two cases. You should only |
| * unwrap a compound command if you created it for that purpose, e.g., |
| * |
| * <pre> |
| * CompoundCommand subcommands = new CompoundCommand(); |
| * subcommands.append(x); |
| * if (condition) |
| * subcommands.append(y); |
| * Command result = subcommands.unwrap(); |
| * </pre> |
| * |
| * is a good way to create an efficient accumulated result. |
| * |
| * @return the unwapped command. |
| */ |
| public Command unwrap() { |
| switch (commandList.size()) { |
| case 0 : { |
| dispose(); |
| return UnexecutableCommand.INSTANCE; |
| } |
| case 1 : { |
| Command result = (Command) commandList.remove(0); |
| dispose(); |
| return result; |
| } |
| default : { |
| return this; |
| } |
| } |
| } |
| |
| /* |
| * Javadoc copied from base class. |
| */ |
| public String toString() { |
| StringBuffer result = new StringBuffer(super.toString()); |
| result.append("" + commandList.size() + ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| result.append("" + resultIndex + ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| return result.toString(); |
| } |
| } |