/**
* <copyright>
*
* Copyright (c) 2002, 2010 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 - Initial API and implementation
*
* </copyright>
*
* $Id: AbstractCommand.java,v 1.5 2006/12/05 20:19:54 emerks Exp $
*/
package net.enilink.komma.common.command;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.enilink.komma.common.CommonPlugin;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
/**
* An abstract implementation of a basic command. Each derived class
* <bold>must</bold> implement {@link ICommand#execute} and
* {@link ICommand#redo}, <bold>must</bold> either implement {@link #undo} or
* implement {@link #canUndo} to return false, and <bold>must</bold> either
* override {@link #prepare} (this is the preferred approach) or can override
* {@link #canExecute} directly.
*
* <p>
* It is very convenient to IUndoContextuse prepare, as it is guaranteed to be
* called only once just before canExecute is to be tested. It can be
* implemented to create any additional commands that need to be executed, and
* the result it yields becomes the permanent cached return value for
* canExecute.
*
*/
public abstract class AbstractCommand implements ICommand, IUndoableOperation {
List<IUndoContext> contexts = new ArrayList<>();
/**
* The label of this command. May be <code>null</code>.
*/
protected String label;
/**
* Keeps track of whether prepare needs to be called. It is tested in
* {@link #canExecute} so that {@link #prepare} is called exactly once to
* ready the command for execution.
*/
protected boolean isPrepared;
/**
* Keeps track of whether the command is executable. It is set in
* {@link #canExecute} to the result of calling {@link #prepare}.
*/
protected boolean isExecutable;
/**
* Holds a short textual description of the command as returned by
* {@link #getDescription} and set by {@link #setDescription}.
*/
protected String description;
private CommandResult commandResult;
/**
* Creates an instance with a default label.
*
*/
protected AbstractCommand() {
}
/**
* Creates an instance with the given label.
*
* @param label
* the label.
*/
protected AbstractCommand(String label) {
this.label = label;
}
/**
* Creates and instance with the given label and description.
*
* @param label
* the label.
* @param description
* the description.
*/
protected AbstractCommand(String label, String description) {
this(label);
this.description = description;
}
public void addContext(IUndoContext context) {
if (!contexts.contains(context)) {
contexts.add(context);
}
}
public final IUndoContext[] getContexts() {
return (IUndoContext[]) contexts.toArray(new IUndoContext[contexts
.size()]);
}
public final boolean hasContext(IUndoContext context) {
Assert.isNotNull(context);
for (int i = 0; i < contexts.size(); i++) {
IUndoContext otherContext = (IUndoContext) contexts.get(i);
// have to check both ways because one context may be more general
// in
// its matching rules than another.
if (context.matches(otherContext) || otherContext.matches(context)) {
return true;
}
}
return false;
}
public void removeContext(IUndoContext context) {
contexts.remove(context);
}
/**
* Called at most once in {@link #canExecute} to give the command an
* opportunity to ready itself for execution. The returned value is stored
* in {@link #canExecute}. In other words, you can override this method to
* initialize and to yield a cached value for the all subsequent calls to
* canExecute.
*
* @return whether the command is executable.
*/
protected boolean prepare() {
return false;
}
/**
* Calls {@link #prepare}, caches the result in {@link #isExecutable}, and
* sets {@link #isPrepared} to <code>true</code>; from then on, it will
* yield the value of isExecutable.
*
* @return whether the command can execute.
*/
@Override
public boolean canExecute() {
if (!isPrepared) {
isExecutable = prepare();
isPrepared = true;
}
return isExecutable;
}
/**
* Returns <code>true</code> because most command should be undoable.
*
* @return <code>true</code>.
*/
public boolean canUndo() {
return true;
}
/**
* Returns <code>true</code> because most command should be redoable.
*
* @return <code>true</code>.
*/
public boolean canRedo() {
return true;
}
/**
* Delegates to {@link #doExecuteWithResult(IProgressMonitor, IAdaptable)}
* and sets the command result.
*/
@Override
public IStatus execute(IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
IProgressMonitor monitor = progressMonitor != null ? progressMonitor
: new NullProgressMonitor();
CommandResult result = doExecuteWithResult(monitor, info);
setResult(result);
return result != null ? result.getStatus() : Status.OK_STATUS;
}
/**
* Performs the actual work of executing this command. Subclasses must
* implement this method to perform some operation.
*
* @param progressMonitor
* the progress monitor provided by the operation history. Must
* never be <code>null</code>.
* @param info
* the IAdaptable (or <code>null</code>) provided by the caller
* in order to supply UI information for prompting the user if
* necessary. When this parameter is not <code>null</code>, it
* should minimally contain an adapter for the
* org.eclipse.swt.widgets.Shell.class.
*
* @return The result of executing this command. May be <code>null</code> if
* the execution status is OK, but there is no meaningful result to
* be returned.
*
* @throws ExecutionException
* if, for some reason, I fail to complete the operation
*/
protected abstract CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException;
@Override
public Collection<?> getAffectedObjects() {
CommandResult result = getCommandResult();
if (result == null) {
return Collections.emptyList();
} else {
List<Object> objects = new ArrayList<>();
for (Object value : result.getReturnValues()) {
if (value != null) {
objects.add(value);
}
}
return objects;
}
}
@Override
public Collection<?> getAffectedResources(Object type) {
return Collections.emptyList();
}
/*
* Javadoc copied from interface.
*/
@Override
public String getDescription() {
return description == null ? CommonPlugin.INSTANCE
.getString("_UI_AbstractCommand_description") : description;
}
/**
* Sets the description after construction.
*
* @param description
* the new description.
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Delegates to {@link #doRedoWithResult(IProgressMonitor, IAdaptable)} and
* sets the command result.
*/
public IStatus redo(IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
IProgressMonitor monitor = progressMonitor != null ? progressMonitor
: new NullProgressMonitor();
CommandResult result = doRedoWithResult(monitor, info);
setResult(result);
return result != null ? result.getStatus() : Status.OK_STATUS;
}
/**
* Performs the actual work of redoing this command. Subclasses must
* implement this method to perform the redo.
*
* @param progressMonitor
* the progress monitor provided by the operation history. Must
* never be <code>null</code>.
* @param info
* the IAdaptable (or <code>null</code>) provided by the caller
* in order to supply UI information for prompting the user if
* necessary. When this parameter is not <code>null</code>, it
* should minimally contain an adapter for the
* org.eclipse.swt.widgets.Shell.class.
*
* @return The result of redoing this command. May be <code>null</code> if
* the execution status is OK, but there is no meaningful result to
* be returned.
*
* @throws ExecutionException
* on failure to redo
*/
protected abstract CommandResult doRedoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException;
/**
* Delegates to {@link #doUndoWithResult(IProgressMonitor, IAdaptable)} and
* sets the command result.
*/
public IStatus undo(IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
IProgressMonitor monitor = progressMonitor != null ? progressMonitor
: new NullProgressMonitor();
CommandResult result = doUndoWithResult(monitor, info);
setResult(result);
return result != null ? result.getStatus() : Status.OK_STATUS;
}
/**
* Performs the actual work of undoing this command. Subclasses must
* implement this method to perform the undo.
*
* @param progressMonitor
* the progress monitor provided by the operation history. Must
* never be <code>null</code>.
* @param info
* the IAdaptable (or <code>null</code>) provided by the caller
* in order to supply UI information for prompting the user if
* necessary. When this parameter is not <code>null</code>, it
* should minimally contain an adapter for the
* org.eclipse.swt.widgets.Shell.class.
*
* @return The result of undoing this command. May be <code>null</code> if
* the execution status is OK, but there is no meaningful result to
* be returned.
*
* @throws ExecutionException
* on failure to undo
*/
protected abstract CommandResult doUndoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException;
/**
* Creates a new compound command, containing this command and the given
* command, that delegates chain to {@link ExtendedCompositeCommand#append}.
*
* @param command
* the command to chain with this one.
* @return a new chained compound command.
*/
@Override
public ICommand compose(IUndoableOperation operation) {
CompositeCommand result = new CompositeCommand();
result.add(this);
result.add(operation);
return result;
}
// Documentation copied from the interface
@Override
public CommandResult getCommandResult() {
return commandResult;
}
/**
* Sets the command result.
*
* @param result
* the new result for this command.
*/
protected final void setResult(CommandResult result) {
this.commandResult = result;
}
public String getLabel() {
return label != null ? label : CommonPlugin.INSTANCE
.getString("_UI_AbstractCommand_label");
}
/**
* Set the label of the command to the specified name.
*
* @param name
* the string to be used for the label.
*/
public void setLabel(String name) {
label = name;
}
/*
* Javadoc copied from interface.
*/
@Override
public void dispose() {
// Do nothing.
}
/**
* Returns an abbreviated name using this object's own class' name, without
* package qualification, followed by a space separated list of
* <tt>field:value</tt> pairs.
*
* @return string representation.
*/
@Override
public String toString() {
String className = getClass().getName();
int lastDotIndex = className.lastIndexOf('.');
StringBuffer result = new StringBuffer(lastDotIndex == -1 ? className
: className.substring(lastDotIndex + 1));
result.append(" (label: " + getLabel() + ")");
result.append(" (description: " + description + ")");
result.append(" (isPrepared: " + isPrepared + ")");
result.append(" (isExecutable: " + isExecutable + ")");
return result.toString();
}
@Override
public ICommand reduce() {
return this;
}
/**
* A marker interface implemented by commands that don't dirty the model.
*/
public static interface INonDirtying {
// This is just a marker interface.
}
/**
* A marker interface implemented by commands that don't want their changes
* to be recorded.
*/
public static interface INoChangeRecording {
// This is just a marker interface.
}
}