diff org.eclipse.core.commands/src/org/eclipse/core/commands/Command.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 735224fcc45f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.commands/src/org/eclipse/core/commands/Command.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1131 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.core.commands.Command;
+
+// import java.io.BufferedWriter;
+// import java.io.IOException;
+// import java.io.StringWriter;
+
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+
+import org.eclipse.core.commands.IParameter;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.CommandEvent;
+import org.eclipse.core.commands.State;
+import org.eclipse.core.commands.Category;
+import org.eclipse.core.commands.NamedHandleObjectWithState;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ParameterType;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.ICommandListener;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.core.commands.IObjectWithState;
+import org.eclipse.core.commands.IExecutionListenerWithChecks;
+import org.eclipse.core.commands.ITypedParameter;
+import org.eclipse.core.commands.HandlerEvent;
+
+import java.lang.all;
+import tango.io.Stdout;
+
+/**
+ * <p>
+ * A command is an abstract representation for some semantic behaviour. It is
+ * not the actual implementation of this behaviour, nor is it the visual
+ * appearance of this behaviour in the user interface. Instead, it is a bridge
+ * between the two.
+ * </p>
+ * <p>
+ * The concept of a command is based on the command design pattern. The notable
+ * difference is how the command delegates responsibility for execution. Rather
+ * than allowing concrete subclasses, it uses a handler mechanism (see the
+ * <code>handlers</code> extension point). This provides another level of
+ * indirection.
+ * </p>
+ * <p>
+ * A command will exist in two states: defined and undefined. A command is
+ * defined if it is declared in the XML of a resolved plug-in. If the plug-in is
+ * unloaded or the command is simply not declared, then it is undefined. Trying
+ * to reference an undefined command will succeed, but trying to access any of
+ * its functionality will fail with a <code>NotDefinedException</code>. If
+ * you need to know when a command changes from defined to undefined (or vice
+ * versa), then attach a command listener.
+ * </p>
+ * <p>
+ * Commands are mutable and will change as their definition changes.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Command : NamedHandleObjectWithState,
+        Comparable {
+
+    /**
+     * This flag can be set to <code>true</code> if commands should print
+     * information to <code>System.out</code> when executing.
+     */
+    public static bool DEBUG_COMMAND_EXECUTION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if commands should print
+     * information to <code>System.out</code> when changing handlers.
+     */
+    public static bool DEBUG_HANDLERS = false;
+
+    /**
+     * This flag can be set to a particular command identifier if only that
+     * command should print information to <code>System.out</code> when
+     * changing handlers.
+     */
+    public static String DEBUG_HANDLERS_COMMAND_ID = null;
+
+    /**
+     * The category to which this command belongs. This value should not be
+     * <code>null</code> unless the command is undefined.
+     */
+    private Category category = null;
+
+    /**
+     * A collection of objects listening to the execution of this command. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private /+transient+/ ListenerList executionListeners = null;
+
+    /**
+     * The handler currently associated with this command. This value may be
+     * <code>null</code> if there is no handler currently.
+     */
+    private /+transient+/ IHandler handler = null;
+
+    /**
+     * The help context identifier for this command. This can be
+     * <code>null</code> if there is no help currently associated with the
+     * command.
+     *
+     * @since 3.2
+     */
+    private String helpContextId;
+
+    /**
+     * The ordered array of parameters understood by this command. This value
+     * may be <code>null</code> if there are no parameters, or if the command
+     * is undefined. It may also be empty.
+     */
+    private IParameter[] parameters = null;
+
+    /**
+     * The type of the return value of this command. This value may be
+     * <code>null</code> if the command does not declare a return type.
+     *
+     * @since 3.2
+     */
+    private ParameterType returnType = null;
+
+    /**
+     * Our command will listen to the active handler for enablement changes so
+     * that they can be fired from the command itself.
+     *
+     * @since 3.3
+     */
+    private IHandlerListener handlerListener;
+
+    /**
+     * Constructs a new instance of <code>Command</code> based on the given
+     * identifier. When a command is first constructed, it is undefined.
+     * Commands should only be constructed by the <code>CommandManager</code>
+     * to ensure that the identifier remains unique.
+     *
+     * @param id
+     *            The identifier for the command. This value must not be
+     *            <code>null</code>, and must be unique amongst all commands.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this command that will be notified when this command's
+     * state changes.
+     *
+     * @param commandListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addCommandListener(ICommandListener commandListener) {
+        if (commandListener is null) {
+            throw new NullPointerException("Cannot add a null command listener"); //$NON-NLS-1$
+        }
+        addListenerObject(cast(Object)commandListener);
+    }
+
+    /**
+     * Adds a listener to this command that will be notified when this command
+     * is about to execute.
+     *
+     * @param executionListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addExecutionListener(
+            IExecutionListener executionListener) {
+        if (executionListener is null) {
+            throw new NullPointerException(
+                    "Cannot add a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            executionListeners = new ListenerList(ListenerList.IDENTITY);
+        }
+
+        executionListeners.add(cast(Object)executionListener);
+    }
+
+    /**
+     * <p>
+     * Adds a state to this command. This will add this state to the active
+     * handler, if the active handler is an instance of {@link IObjectWithState}.
+     * </p>
+     * <p>
+     * A single instance of {@link State} cannot be registered with multiple
+     * commands. Each command requires its own unique instance.
+     * </p>
+     *
+     * @param id
+     *            The identifier of the state to add; must not be
+     *            <code>null</code>.
+     * @param state
+     *            The state to add; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public override void addState(String id, State state) {
+        super.addState(id, state);
+        state.setId(id);
+        if ( auto h = cast(IObjectWithState)handler) {
+            h.addState(id, state);
+        }
+    }
+
+    /**
+     * Compares this command with another command by comparing each of its
+     * non-transient attributes.
+     *
+     * @param object
+     *            The object with which to compare; must be an instance of
+     *            <code>Command</code>.
+     * @return A negative integer, zero or a postivie integer, if the object is
+     *         greater than, equal to or less than this command.
+     */
+    public final int compareTo(Object object) {
+        Command castedObject = cast(Command) object;
+        int compareTo = Util.compare(category, castedObject.category);
+        if (compareTo is 0) {
+            compareTo = Util.compare(defined, castedObject.defined);
+            if (compareTo is 0) {
+                compareTo = Util.compare(description, castedObject.description);
+                if (compareTo is 0) {
+                    compareTo = Util.compare(cast(Object)handler, cast(Object)castedObject.handler);
+                    if (compareTo is 0) {
+                        compareTo = Util.compare(id, castedObject.id);
+                        if (compareTo is 0) {
+                            compareTo = Util.compare(name, castedObject.name);
+                            if (compareTo is 0) {
+                                Object[] left, right;
+                                foreach( p; parameters ){
+                                    left ~= cast(Object)p;
+                                }
+                                foreach( p; castedObject.parameters ){
+                                    right ~= cast(Object)p;
+                                }
+                                compareTo = Util.compare(left,
+                                        right);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category) {
+        define(name, description, category, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters) {
+        define(name, description, category, parameters, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     * @param returnType
+     *            The type of value returned by this command. This value may be
+     *            <code>null</code> if the command does not declare a return
+     *            type.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters,
+            ParameterType returnType) {
+        define(name, description, category, parameters, returnType, null);
+    }
+
+    /**
+     * <p>
+     * Defines this command by giving it a name, and possibly a description as
+     * well. The defined property automatically becomes <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param name
+     *            The name of this command; must not be <code>null</code>.
+     * @param description
+     *            The description for this command; may be <code>null</code>.
+     * @param category
+     *            The category for this command; must not be <code>null</code>.
+     * @param parameters
+     *            The parameters understood by this command. This value may be
+     *            either <code>null</code> or empty if the command does not
+     *            accept parameters.
+     * @param returnType
+     *            The type of value returned by this command. This value may be
+     *            <code>null</code> if the command does not declare a return
+     *            type.
+     * @param helpContextId
+     *            The identifier of the help context to associate with this
+     *            command; may be <code>null</code> if this command does not
+     *            have any help associated with it.
+     * @since 3.2
+     */
+    public final void define(String name, String description,
+            Category category, IParameter[] parameters,
+            ParameterType returnType, String helpContextId) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a command cannot be null"); //$NON-NLS-1$
+        }
+
+        if (category is null) {
+            throw new NullPointerException(
+                    "The category of a command cannot be null"); //$NON-NLS-1$
+        }
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        bool nameChanged = !Util.equals(this.name, name);
+        this.name = name;
+
+        bool descriptionChanged = !Util.equals(this.description,
+                description);
+        this.description = description;
+
+        bool categoryChanged = !Util.equals(this.category, category);
+        this.category = category;
+
+        Object[] pLeft, pRight;
+        foreach( p; this.parameters ){
+            pLeft ~= cast(Object)p;
+        }
+        foreach( p; parameters ){
+            pRight ~= cast(Object)p;
+        }
+        bool parametersChanged = !Util.equals(pLeft,
+                pRight);
+        this.parameters = parameters;
+
+        bool returnTypeChanged = !Util.equals(this.returnType,
+                returnType);
+        this.returnType = returnType;
+
+        bool helpContextIdChanged = !Util.equals(this.helpContextId,
+                helpContextId);
+        this.helpContextId = helpContextId;
+
+        fireCommandChanged(new CommandEvent(this, categoryChanged,
+                definedChanged, descriptionChanged, false, nameChanged,
+                parametersChanged, returnTypeChanged, helpContextIdChanged));
+    }
+
+    /**
+     * Executes this command by delegating to the current handler, if any. If
+     * the debugging flag is set, then this method prints information about
+     * which handler is selected for performing this command. This method will
+     * succeed regardless of whether the command is enabled or defined. It is
+     * generally preferred to call {@link #executeWithChecks(ExecutionEvent)}.
+     *
+     * @param event
+     *            An event containing all the information about the current
+     *            state of the application; must not be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>. This
+     *         result will be available to the client executing the command, and
+     *         execution listeners.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @deprecated Please use {@link #executeWithChecks(ExecutionEvent)}
+     *             instead.
+     */
+    public final Object execute(ExecutionEvent event) {
+        firePreExecute(event);
+        IHandler handler = this.handler;
+
+        // Perform the execution, if there is a handler.
+        if ((handler !is null) && (handler.isHandled())) {
+            try {
+                Object returnValue = handler.execute(event);
+                firePostExecuteSuccess(returnValue);
+                return returnValue;
+            } catch (ExecutionException e) {
+                firePostExecuteFailure(e);
+                throw e;
+            }
+        }
+
+        NotHandledException e = new NotHandledException(
+                "There is no handler to execute. " ~ getId()); //$NON-NLS-1$
+        fireNotHandled(e);
+        throw e;
+    }
+
+    /**
+     * Executes this command by delegating to the current handler, if any. If
+     * the debugging flag is set, then this method prints information about
+     * which handler is selected for performing this command. This does checks
+     * to see if the command is enabled and defined. If it is not both enabled
+     * and defined, then the execution listeners will be notified and an
+     * exception thrown.
+     *
+     * @param event
+     *            An event containing all the information about the current
+     *            state of the application; must not be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>. This
+     *         result will be available to the client executing the command, and
+     *         execution listeners.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotDefinedException
+     *             If the command you are trying to execute is not defined.
+     * @throws NotEnabledException
+     *             If the command you are trying to execute is not enabled.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @since 3.2
+     */
+    public final Object executeWithChecks(ExecutionEvent event) {
+        firePreExecute(event);
+        IHandler handler = this.handler;
+
+        if (!isDefined()) {
+            NotDefinedException exception = new NotDefinedException(
+                    "Trying to execute a command that is not defined. " //$NON-NLS-1$
+                            ~ getId());
+            fireNotDefined(exception);
+            throw exception;
+        }
+
+        // Perform the execution, if there is a handler.
+        if ((handler !is null) && (handler.isHandled())) {
+            setEnabled(event.getApplicationContext());
+            if (!isEnabled()) {
+                NotEnabledException exception = new NotEnabledException(
+                        "Trying to execute the disabled command " ~ getId()); //$NON-NLS-1$
+                fireNotEnabled(exception);
+                throw exception;
+            }
+
+            try {
+                Object returnValue = handler.execute(event);
+                firePostExecuteSuccess(returnValue);
+                return returnValue;
+            } catch (ExecutionException e) {
+                firePostExecuteFailure(e);
+                throw e;
+            }
+        }
+
+        NotHandledException e = new NotHandledException(
+                "There is no handler to execute for command " ~ getId()); //$NON-NLS-1$
+        fireNotHandled(e);
+        throw e;
+    }
+
+    /**
+     * Notifies the listeners for this command that it has changed in some way.
+     *
+     * @param commandEvent
+     *            The event to send to all of the listener; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCommandChanged(CommandEvent commandEvent) {
+        if (commandEvent is null) {
+            throw new NullPointerException("Cannot fire a null event"); //$NON-NLS-1$
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            ICommandListener listener = cast(ICommandListener) listeners[i];
+            SafeRunner.run(new class(listener) ISafeRunnable {
+                ICommandListener listener_;
+                this(ICommandListener a){ this.listener_ = a; }
+                public void handleException(Exception exception) {
+                }
+
+                public void run() {
+                    listener_.commandChanged(commandEvent);
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because the command is not defined.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    private final void fireNotDefined(NotDefinedException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not defined: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                Object object = listeners[i];
+                if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                    listener.notDefined(getId(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because there is no handler.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    private final void fireNotEnabled(NotEnabledException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not enabled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                Object object = listeners[i];
+                if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
+                    listener.notEnabled(getId(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed because there is no handler.
+     *
+     * @param e
+     *            The exception that is about to be thrown; never
+     *            <code>null</code>.
+     */
+    private final void fireNotHandled(NotHandledException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "not handled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.notHandled(getId(), e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute has failed during the execution.
+     *
+     * @param e
+     *            The exception that has been thrown; never <code>null</code>.
+     *            After this method completes, the exception will be thrown
+     *            again.
+     */
+    private final void firePostExecuteFailure(ExecutionException e) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "failure: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.postExecuteFailure(getId(), e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an execution has
+     * completed successfully.
+     *
+     * @param returnValue
+     *            The return value from the command; may be <code>null</code>.
+     */
+    private final void firePostExecuteSuccess(Object returnValue) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "success: id=" ~ getId() ~ "; returnValue=" //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ returnValue.toString );
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.postExecuteSuccess(getId(), returnValue);
+            }
+        }
+    }
+
+    /**
+     * Notifies the execution listeners for this command that an attempt to
+     * execute is about to start.
+     *
+     * @param event
+     *            The execution event that will be used; never <code>null</code>.
+     */
+    private final void firePreExecute(ExecutionEvent event) {
+        // Debugging output
+        if (DEBUG_COMMAND_EXECUTION) {
+            Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ "starting: id=" ~ getId() ~ "; event=" ~ event.toString ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        if (executionListeners !is null) {
+            Object[] listeners = executionListeners.getListeners();
+            for (int i = 0; i < listeners.length; i++) {
+                IExecutionListener listener = cast(IExecutionListener) listeners[i];
+                listener.preExecute(getId(), event);
+            }
+        }
+    }
+
+    /**
+     * Returns the category for this command.
+     *
+     * @return The category for this command; never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public final Category getCategory() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the category from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        return category;
+    }
+
+    /**
+     * Returns the current handler for this command. This is used by the command
+     * manager for determining the appropriate help context identifiers and by
+     * the command service to allow handlers to update elements.
+     * <p>
+     * This value can change at any time and should never be cached.
+     * </p>
+     *
+     * @return The current handler for this command; may be <code>null</code>.
+     * @since 3.3
+     */
+    public final IHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Returns the help context identifier associated with this command. This
+     * method should not be called by clients. Clients should use
+     * {@link CommandManager#getHelpContextId(Command)} instead.
+     *
+     * @return The help context identifier for this command; may be
+     *         <code>null</code> if there is none.
+     * @since 3.2
+     */
+    final String getHelpContextId() {
+        return helpContextId;
+    }
+
+    /**
+     * Returns the parameter with the provided id or <code>null</code> if this
+     * command does not have a parameter with the id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve.
+     * @return The parameter with the provided id or <code>null</code> if this
+     *         command does not have a parameter with the id.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final IParameter getParameter(String parameterId) {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get a parameter from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        if (parameters is null) {
+            return null;
+        }
+
+        for (int i = 0; i < parameters.length; i++) {
+            IParameter parameter = parameters[i];
+            if (parameter.getId().equals(parameterId)) {
+                return parameter;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the parameters for this command. This call triggers provides a
+     * copy of the array, so excessive calls to this method should be avoided.
+     *
+     * @return The parameters for this command. This value might be
+     *         <code>null</code>, if the command has no parameters.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public final IParameter[] getParameters() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the parameters from an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        if ((parameters is null) || (parameters.length is 0)) {
+            return null;
+        }
+
+        IParameter[] returnValue = new IParameter[parameters.length];
+        SimpleType!(IParameter).arraycopy(parameters, 0, returnValue, 0, parameters.length);
+        return returnValue;
+    }
+
+    /**
+     * Returns the {@link ParameterType} for the parameter with the provided id
+     * or <code>null</code> if this command does not have a parameter type
+     * with the id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve the {@link ParameterType}
+     *            of.
+     * @return The {@link ParameterType} for the parameter with the provided id
+     *         or <code>null</code> if this command does not have a parameter
+     *         type with the provided id.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final ParameterType getParameterType(String parameterId) {
+        IParameter parameter = getParameter(parameterId);
+        if ( auto parameterWithType = cast(ITypedParameter)parameter ) {
+            return parameterWithType.getParameterType();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the {@link ParameterType} for the return value of this command or
+     * <code>null</code> if this command does not declare a return value
+     * parameter type.
+     *
+     * @return The {@link ParameterType} for the return value of this command or
+     *         <code>null</code> if this command does not declare a return
+     *         value parameter type.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     * @since 3.2
+     */
+    public final ParameterType getReturnType() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the return type of an undefined command. " //$NON-NLS-1$
+                            ~ id);
+        }
+
+        return returnType;
+    }
+
+    /**
+     * Returns whether this command has a handler, and whether this handler is
+     * also handled and enabled.
+     *
+     * @return <code>true</code> if the command is handled; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isEnabled() {
+        if (handler is null) {
+            return false;
+        }
+
+        try {
+            return handler.isEnabled();
+        } catch (Exception e) {
+            if (DEBUG_HANDLERS) {
+                // since this has the ability to generate megs of logs, only
+                // provide information if tracing
+                Tracing.printTrace("HANDLERS", "Handler " ~ (cast(Object)handler).toString()  ~ " for "  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+                        ~ id ~ " threw unexpected exception"); //$NON-NLS-1$
+                ExceptionPrintStackTrace( e, Stdout );
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called be the framework to allow the handler to update its enabled state.
+     *
+     * @param evaluationContext
+     *            the state to evaluate against. May be <code>null</code>
+     *            which indicates that the handler can query whatever model that
+     *            is necessary.  This context must not be cached.
+     * @since 3.4
+     */
+    public void setEnabled(Object evaluationContext) {
+        if (null !is cast(IHandler2)handler ) {
+            (cast(IHandler2) handler).setEnabled(evaluationContext);
+        }
+    }
+
+    /**
+     * Returns whether this command has a handler, and whether this handler is
+     * also handled.
+     *
+     * @return <code>true</code> if the command is handled; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isHandled() {
+        if (handler is null) {
+            return false;
+        }
+
+        return handler.isHandled();
+    }
+
+    /**
+     * Removes a listener from this command.
+     *
+     * @param commandListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeCommandListener(
+            ICommandListener commandListener) {
+        if (commandListener is null) {
+            throw new NullPointerException(
+                    "Cannot remove a null command listener"); //$NON-NLS-1$
+        }
+
+        removeListenerObject(cast(Object)commandListener);
+    }
+
+    /**
+     * Removes a listener from this command.
+     *
+     * @param executionListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeExecutionListener(
+            IExecutionListener executionListener) {
+        if (executionListener is null) {
+            throw new NullPointerException(
+                    "Cannot remove a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners !is null) {
+            executionListeners.remove(cast(Object)executionListener);
+            if (executionListeners.isEmpty()) {
+                executionListeners = null;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a state from this command. This will remove the state from the
+     * active handler, if the active handler is an instance of
+     * {@link IObjectWithState}.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier of the state to remove; must not be
+     *            <code>null</code>.
+     * @since 3.2
+     */
+    public override void removeState(String stateId) {
+        if ( auto h = cast(IObjectWithState)handler ) {
+            h.removeState(stateId);
+        }
+        super.removeState(stateId);
+    }
+
+    /**
+     * Changes the handler for this command. This will remove all the state from
+     * the currently active handler (if any), and add it to <code>handler</code>.
+     * If debugging is turned on, then this will also print information about
+     * the change to <code>System.out</code>.
+     *
+     * @param handler
+     *            The new handler; may be <code>null</code> if none.
+     * @return <code>true</code> if the handler changed; <code>false</code>
+     *         otherwise.
+     */
+    public final bool setHandler(IHandler handler) {
+        if (Util.equals(cast(Object)handler, cast(Object)this.handler)) {
+            return false;
+        }
+
+        // Swap the state around.
+        String[] stateIds = getStateIds();
+        if (stateIds !is null) {
+            for (int i = 0; i < stateIds.length; i++) {
+                String stateId = stateIds[i];
+                if ( auto h = cast(IObjectWithState)this.handler ) {
+                    h.removeState(stateId);
+                }
+                if ( auto h = cast(IObjectWithState)handler ) {
+                    State stateToAdd = getState(stateId);
+                    h.addState(stateId, stateToAdd);
+                }
+            }
+        }
+
+        bool enabled = isEnabled();
+        if (this.handler !is null) {
+            this.handler.removeHandlerListener(getHandlerListener());
+        }
+
+        // Update the handler, and flush the string representation.
+        this.handler = handler;
+        if (this.handler !is null) {
+            this.handler.addHandlerListener(getHandlerListener());
+        }
+        string = null;
+
+        // Debugging output
+        if ((DEBUG_HANDLERS)
+                && ((DEBUG_HANDLERS_COMMAND_ID is null) || (DEBUG_HANDLERS_COMMAND_ID
+                        .equals(id)))) {
+            StringBuffer buffer = new StringBuffer("Command('"); //$NON-NLS-1$
+            buffer.append(id);
+            buffer.append("') has changed to "); //$NON-NLS-1$
+            if (handler is null) {
+                buffer.append("no handler"); //$NON-NLS-1$
+            } else {
+                buffer.append('\'');
+                buffer.append(( cast(Object)handler).toString);
+                buffer.append("' as its handler"); //$NON-NLS-1$
+            }
+            Tracing.printTrace("HANDLERS", buffer.toString()); //$NON-NLS-1$
+        }
+
+        // Send notification
+        fireCommandChanged(new CommandEvent(this, false, false, false, true,
+                false, false, false, false, enabled !is isEnabled()));
+
+        return true;
+    }
+
+    /**
+     * @return the handler listener
+     */
+    private IHandlerListener getHandlerListener() {
+        if (handlerListener is null) {
+            handlerListener = new class IHandlerListener {
+                public void handlerChanged(HandlerEvent handlerEvent) {
+                    bool enabledChanged = handlerEvent.isEnabledChanged();
+                    bool handledChanged = handlerEvent.isHandledChanged();
+                    fireCommandChanged(new CommandEvent(this.outer, false,
+                            false, false, handledChanged, false, false, false,
+                            false, enabledChanged));
+                }
+            };
+        }
+        return handlerListener;
+    }
+
+    /**
+     * The string representation of this command -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override final String toString() {
+        if (string is null) {
+            String parms;
+            foreach( p; parameters ){
+                parms ~= "{"~(cast(Object)p).toString~"}";
+            }
+            string = Format("Command({},{},\n\t\t{},\n\t\t{},\n\t\t{},\n\t\t{},{},{})",
+                id,
+                name is null ? "":name,
+                description is null?"":description,
+                category is null?"":category.toString(),
+                handler is null?"": (cast(Object)handler).toString(),
+                parms,
+                returnType is null?"":returnType.toString(),
+                defined
+            );
+        }
+        return string;
+    }
+
+    /**
+     * Makes this command become undefined. This has the side effect of changing
+     * the name and description to <code>null</code>. This also removes all
+     * state and disposes of it. Notification is sent to all listeners.
+     */
+    public override final void undefine() {
+        bool enabledChanged = isEnabled();
+
+        string = null;
+
+        bool definedChanged = defined;
+        defined = false;
+
+        bool nameChanged = name !is null;
+        name = null;
+
+        bool descriptionChanged = description !is null;
+        description = null;
+
+        bool categoryChanged = category !is null;
+        category = null;
+
+        bool parametersChanged = parameters !is null;
+        parameters = null;
+
+        bool returnTypeChanged = returnType !is null;
+        returnType = null;
+
+        String[] stateIds = getStateIds();
+        if (stateIds !is null) {
+            if ( auto handlerWithState = cast(IObjectWithState)handler ) {
+                for (int i = 0; i < stateIds.length; i++) {
+                    String stateId = stateIds[i];
+                    handlerWithState.removeState(stateId);
+
+                    State state = getState(stateId);
+                    removeState(stateId);
+                    state.dispose();
+                }
+            } else {
+                for (int i = 0; i < stateIds.length; i++) {
+                    String stateId = stateIds[i];
+                    State state = getState(stateId);
+                    removeState(stateId);
+                    state.dispose();
+                }
+            }
+        }
+
+        fireCommandChanged(new CommandEvent(this, categoryChanged,
+                definedChanged, descriptionChanged, false, nameChanged,
+                parametersChanged, returnTypeChanged, false, enabledChanged));
+    }
+}