changeset 3:6518c18a01f7

eclipse.core package without osgi dependencies
author Frank Benoit <benoit@tionex.de>
date Wed, 26 Mar 2008 00:57:19 +0100
parents a012107a911c
children c87617952847
files dwtx/core/commands/AbstractHandler.d dwtx/core/commands/AbstractHandlerWithState.d dwtx/core/commands/AbstractParameterValueConverter.d dwtx/core/commands/Category.d dwtx/core/commands/CategoryEvent.d dwtx/core/commands/Command.d dwtx/core/commands/CommandEvent.d dwtx/core/commands/CommandManager.d dwtx/core/commands/CommandManagerEvent.d dwtx/core/commands/ExecutionEvent.d dwtx/core/commands/ExecutionException.d dwtx/core/commands/HandlerEvent.d dwtx/core/commands/ICategoryListener.d dwtx/core/commands/ICommandListener.d dwtx/core/commands/ICommandManagerListener.d dwtx/core/commands/IExecutionListener.d dwtx/core/commands/IExecutionListenerWithChecks.d dwtx/core/commands/IHandler.d dwtx/core/commands/IHandlerAttributes.d dwtx/core/commands/IHandlerListener.d dwtx/core/commands/INamedHandleStateIds.d dwtx/core/commands/IObjectWithState.d dwtx/core/commands/IParameter.d dwtx/core/commands/IParameterTypeListener.d dwtx/core/commands/IParameterValues.d dwtx/core/commands/IStateListener.d dwtx/core/commands/ITypedParameter.d dwtx/core/commands/NamedHandleObjectWithState.d dwtx/core/commands/NotEnabledException.d dwtx/core/commands/NotHandledException.d dwtx/core/commands/ParameterType.d dwtx/core/commands/ParameterTypeEvent.d dwtx/core/commands/ParameterValueConversionException.d dwtx/core/commands/ParameterValuesException.d dwtx/core/commands/Parameterization.d dwtx/core/commands/ParameterizedCommand.d dwtx/core/commands/SerializationException.d dwtx/core/commands/State.d dwtx/core/commands/common/AbstractBitSetEvent.d dwtx/core/commands/common/AbstractHandleObjectEvent.d dwtx/core/commands/common/AbstractNamedHandleEvent.d dwtx/core/commands/common/CommandException.d dwtx/core/commands/common/EventManager.d dwtx/core/commands/common/HandleObject.d dwtx/core/commands/common/HandleObjectManager.d dwtx/core/commands/common/IIdentifiable.d dwtx/core/commands/common/NamedHandleObject.d dwtx/core/commands/common/NamedHandleObjectComparator.d dwtx/core/commands/common/NotDefinedException.d dwtx/core/commands/contexts/Context.d dwtx/core/commands/contexts/ContextEvent.d dwtx/core/commands/contexts/ContextManager.d dwtx/core/commands/contexts/ContextManagerEvent.d dwtx/core/commands/contexts/IContextListener.d dwtx/core/commands/contexts/IContextManagerListener.d dwtx/core/commands/operations/AbstractOperation.d dwtx/core/commands/operations/DefaultOperationHistory.d dwtx/core/commands/operations/IAdvancedUndoableOperation.d dwtx/core/commands/operations/IAdvancedUndoableOperation2.d dwtx/core/commands/operations/ICompositeOperation.d dwtx/core/commands/operations/IContextReplacingOperation.d dwtx/core/commands/operations/IOperationApprover.d dwtx/core/commands/operations/IOperationApprover2.d dwtx/core/commands/operations/IOperationHistory.d dwtx/core/commands/operations/IOperationHistoryListener.d dwtx/core/commands/operations/IUndoContext.d dwtx/core/commands/operations/IUndoableOperation.d dwtx/core/commands/operations/LinearUndoEnforcer.d dwtx/core/commands/operations/LinearUndoViolationDetector.d dwtx/core/commands/operations/ObjectUndoContext.d dwtx/core/commands/operations/OperationHistoryEvent.d dwtx/core/commands/operations/OperationHistoryFactory.d dwtx/core/commands/operations/OperationStatus.d dwtx/core/commands/operations/TriggeredOperations.d dwtx/core/commands/operations/UndoContext.d dwtx/core/commands/util/Tracing.d dwtx/core/internal/commands/operations/GlobalUndoContext.d dwtx/core/internal/commands/util/Util.d dwtx/core/internal/runtime/IRuntimeConstants.d dwtx/core/internal/runtime/LocalizationUtils.d dwtx/core/runtime/Assert.d dwtx/core/runtime/AssertionFailedException.d dwtx/core/runtime/CoreException.d dwtx/core/runtime/IAdaptable.d dwtx/core/runtime/IAdapterFactory.d dwtx/core/runtime/IAdapterManager.d dwtx/core/runtime/ILogListener.d dwtx/core/runtime/IPath.d dwtx/core/runtime/IProgressMonitor.d dwtx/core/runtime/IProgressMonitorWithBlocking.d dwtx/core/runtime/ISafeRunnable.d dwtx/core/runtime/IStatus.d dwtx/core/runtime/ListenerList.d dwtx/core/runtime/MultiStatus.d dwtx/core/runtime/NullProgressMonitor.d dwtx/core/runtime/OperationCanceledException.d dwtx/core/runtime/Path.d dwtx/core/runtime/ProgressMonitorWrapper.d dwtx/core/runtime/QualifiedName.d dwtx/core/runtime/SafeRunner.d dwtx/core/runtime/Status.d dwtx/core/runtime/SubMonitor.d dwtx/core/runtime/SubProgressMonitor.d
diffstat 103 files changed, 17867 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/AbstractHandler.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.AbstractHandler;
+
+import dwtx.core.commands.common.EventManager;
+import dwtx.core.commands.IHandler;
+import dwtx.core.commands.IHandlerListener;
+import dwtx.core.commands.HandlerEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * This class is a partial implementation of <code>IHandler</code>. This
+ * abstract implementation provides support for handler listeners. You should
+ * subclass from this method unless you want to implement your own listener
+ * support. Subclasses should call
+ * {@link AbstractHandler#fireHandlerChanged(HandlerEvent)}when the handler
+ * changes. Subclasses can also override {@link AbstractHandler#isEnabled()} and
+ * {@link AbstractHandler#isHandled()}.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractHandler : EventManager, IHandler {
+
+    /**
+     * @see IHandler#addHandlerListener(IHandlerListener)
+     */
+    public void addHandlerListener(IHandlerListener handlerListener) {
+        addListenerObject(cast(Object)handlerListener);
+    }
+
+    /**
+     * The default implementation does nothing. Subclasses who attach listeners
+     * to other objects are encouraged to detach them in this method.
+     *
+     * @see dwtx.core.commands.IHandler#dispose()
+     */
+    public void dispose() {
+        // Do nothing.
+    }
+
+    /**
+     * Fires an event to all registered listeners describing changes to this
+     * instance.
+     * <p>
+     * Subclasses may extend the definition of this method (i.e., if a different
+     * type of listener can be attached to a subclass). This is used primarily
+     * for support of <code>AbstractHandler</code> in
+     * <code>dwtx.ui.workbench</code>, and clients should be wary of
+     * overriding this behaviour. If this method is overridden, then the first
+     * line of the method should be "<code>super.fireHandlerChanged(handlerEvent);</code>".
+     * </p>
+     *
+     * @param handlerEvent
+     *            the event describing changes to this instance. Must not be
+     *            <code>null</code>.
+     */
+    protected void fireHandlerChanged(HandlerEvent handlerEvent) {
+        if (handlerEvent is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IHandlerListener listener = cast(IHandlerListener) listeners[i];
+            listener.handlerChanged(handlerEvent);
+        }
+    }
+
+    /**
+     * Whether this handler is capable of executing at this time. Subclasses may
+     * override this method.
+     *
+     * @return <code>true</code>
+     */
+    public bool isEnabled() {
+        return true;
+    }
+
+    /**
+     * Whether this handler is capable of handling delegated responsibilities at
+     * this time. Subclasses may override this method.
+     *
+     * @return <code>true</code>
+     */
+    public bool isHandled() {
+        return true;
+    }
+
+    /**
+     * <p>
+     * Returns true iff there is one or more IHandlerListeners attached to this
+     * AbstractHandler.
+     * </p>
+     * <p>
+     * Subclasses may extend the definition of this method (i.e., if a different
+     * type of listener can be attached to a subclass). This is used primarily
+     * for support of <code>AbstractHandler</code> in
+     * <code>dwtx.ui.workbench</code>, and clients should be wary of
+     * overriding this behaviour. If this method is overridden, then the return
+     * value should include "<code>super.hasListeners() ||</code>".
+     * </p>
+     *
+     * @return true iff there is one or more IHandlerListeners attached to this
+     *         AbstractHandler
+     */
+    protected bool hasListeners() {
+        return isListenerAttached();
+    }
+
+    /**
+     * @see IHandler#removeHandlerListener(IHandlerListener)
+     */
+    public void removeHandlerListener(IHandlerListener handlerListener) {
+        removeListenerObject(cast(Object)handlerListener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/AbstractHandlerWithState.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.AbstractHandlerWithState;
+
+import dwtx.core.commands.AbstractHandler;
+import dwtx.core.commands.IObjectWithState;
+import dwtx.core.commands.IStateListener;
+import dwtx.core.commands.State;
+
+import dwt.dwthelper.utils;
+
+import tango.util.collection.HashMap;
+import tango.util.collection.model.Map;
+import tango.util.collection.model.Set;
+
+/**
+ * <p>
+ * An abstract implementation of {@link IObjectWithState}. This provides basic
+ * handling for adding and remove state. When state is added, the handler
+ * attaches itself as a listener and fire a handleStateChange event to notify
+ * this handler. When state is removed, the handler removes itself as a
+ * listener.
+ * </p>
+ * <p>
+ * Clients may extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class AbstractHandlerWithState : AbstractHandler,
+        IObjectWithState, IStateListener {
+
+    /**
+     * The map of states currently held by this handler. If this handler has no
+     * state (generally, when inactive), then this will be <code>null</code>.
+     */
+    private Map!(String,State) states = null;
+
+    /**
+     * <p>
+     * Adds a state to this handler. This will add this handler as a listener to
+     * the state, and then fire a handleStateChange so that the handler can
+     * respond to the incoming state.
+     * </p>
+     * <p>
+     * Clients may extend this method, but they should call this super method
+     * first before doing anything else.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier indicating the type of state being added; must
+     *            not be <code>null</code>.
+     * @param state
+     *            The state to add; must not be <code>null</code>.
+     */
+    public void addState(String stateId, State state) {
+        if (state is null) {
+            throw new NullPointerException("Cannot add a null state"); //$NON-NLS-1$
+        }
+
+        if (states is null) {
+            states = new HashMap!(String,State)(/+3+/);
+        }
+        states.add(stateId, state);
+        state.addListener(this);
+        handleStateChange(state, null);
+    }
+
+    public final State getState(String stateId) {
+        if ((states is null) || (states.drained())) {
+            return null;
+        }
+
+        return cast(State) states.get(stateId);
+    }
+
+    public final String[] getStateIds() {
+        if ((states is null) || (states.drained())) {
+            return null;
+        }
+
+        String[] res;
+        foreach( k, v; states.keys() ){
+            res ~= k;
+        }
+        return res;
+    }
+
+    /**
+     * <p>
+     * Removes a state from this handler. This will remove this handler as a
+     * listener to the state. No event is fired to notify the handler of this
+     * change.
+     * </p>
+     * <p>
+     * Clients may extend this method, but they should call this super method
+     * first before doing anything else.
+     * </p>
+     *
+     * @param stateId
+     *            The identifier of the state to remove; must not be
+     *            <code>null</code>.
+     */
+    public void removeState(String stateId) {
+        if (stateId is null) {
+            throw new NullPointerException("Cannot remove a null state"); //$NON-NLS-1$
+        }
+
+        State state = cast(State) states.get(stateId);
+        if (state !is null) {
+            state.removeListener(this);
+            if (states !is null) {
+                states.remove(state);
+                if (states.drained()) {
+                    states = null;
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/AbstractParameterValueConverter.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.AbstractParameterValueConverter;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * Supports conversion between objects and strings for command parameter values.
+ * Extenders must produce strings that identify objects (of a specific command
+ * parameter type) as well as consume the strings to locate and return the
+ * objects they identify.
+ * </p>
+ * <p>
+ * This class offers multiple handlers of a command a consistent way of
+ * converting string parameter values into the objects that the handlers would
+ * prefer to deal with. This class also gives clients a way to serialize
+ * object parameters as strings so that entire parameterized commands can be
+ * serialized, stored and later deserialized and executed.
+ * </p>
+ * <p>
+ * This class will typically be extended so the subclass can be referenced from
+ * the <code>converter</code> attribute of the
+ * <code>commandParameterType</code> elemement of the
+ * <code>dwtx.ui.commands</code> extension-point. Objects implementing
+ * this interface may also be passed directly to
+ * {@link ParameterType#define(String, AbstractParameterValueConverter)} by
+ * clients.
+ * </p>
+ *
+ * @see ParameterType#define(String, AbstractParameterValueConverter)
+ * @see ParameterizedCommand#serialize()
+ * @since 3.2
+ */
+public abstract class AbstractParameterValueConverter {
+
+    /**
+     * Converts a string encoded command parameter value into the parameter
+     * value object.
+     *
+     * @param parameterValue
+     *            a command parameter value string describing an object; may be
+     *            <code>null</code>
+     * @return the object described by the command parameter value string; may
+     *         be <code>null</code>
+     * @throws ParameterValueConversionException
+     *             if an object cannot be produced from the
+     *             <code>parameterValue</code> string
+     */
+    public abstract Object convertToObject(String parameterValue);
+
+    /**
+     * Converts a command parameter value object into a string that encodes a
+     * reference to the object or serialization of the object.
+     *
+     * @param parameterValue
+     *            an object to convert into an identifying string; may be
+     *            <code>null</code>
+     * @return a string describing the provided object; may be <code>null</code>
+     * @throws ParameterValueConversionException
+     *             if a string reference or serialization cannot be provided for
+     *             the <code>parameterValue</code>
+     */
+    public abstract String convertToString(Object parameterValue);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/Category.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.Category;
+
+import tango.util.collection.ArraySeq;
+
+import dwtx.core.commands.common.NamedHandleObject;
+import dwtx.core.internal.commands.util.Util;
+import dwtx.core.commands.ICategoryListener;
+import dwtx.core.commands.CategoryEvent;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * A logical group for a set of commands. A command belongs to exactly one
+ * category. The category has no functional effect, but may be used in graphical
+ * tools that want to group the set of commands somehow.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Category : NamedHandleObject {
+
+    /**
+     * A collection of objects listening to changes to this category. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private ArraySeq!(ICategoryListener) categoryListeners;
+
+    /**
+     * Constructs a new instance of <code>Category</code> based on the given
+     * identifier. When a category is first constructed, it is undefined.
+     * Category should only be constructed by the <code>CommandManager</code>
+     * to ensure that identifier remain unique.
+     *
+     * @param id
+     *            The identifier for the category. This value must not be
+     *            <code>null</code>, and must be unique amongst all
+     *            categories.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this category that will be notified when this
+     * category's state changes.
+     *
+     * @param categoryListener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addCategoryListener(
+            ICategoryListener categoryListener) {
+        if (categoryListener is null) {
+            throw new NullPointerException();
+        }
+        if (categoryListeners is null) {
+            categoryListeners = new ArraySeq!(ICategoryListener);
+        }
+        if (!categoryListeners.contains(categoryListener)) {
+            categoryListeners.append(categoryListener);
+        }
+    }
+
+    /**
+     * <p>
+     * Defines this category 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>.
+     */
+    public final void define(String name, String description) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name 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;
+
+        fireCategoryChanged(new CategoryEvent(this, definedChanged,
+                descriptionChanged, nameChanged));
+    }
+
+    /**
+     * Notifies the listeners for this category that it has changed in some way.
+     *
+     * @param categoryEvent
+     *            The event to send to all of the listener; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCategoryChanged(CategoryEvent categoryEvent) {
+        if (categoryEvent is null) {
+            throw new NullPointerException();
+        }
+        if (categoryListeners !is null) {
+            foreach( listener; categoryListeners ){
+                listener.categoryChanged(categoryEvent);
+            }
+        }
+    }
+
+    /**
+     * Removes a listener from this category.
+     *
+     * @param categoryListener
+     *            The listener to be removed; must not be <code>null</code>.
+     *
+     */
+    public final void removeCategoryListener(
+            ICategoryListener categoryListener) {
+        if (categoryListener is null) {
+            throw new NullPointerException();
+        }
+
+        if (categoryListeners !is null) {
+            categoryListeners.remove(categoryListener);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.common.HandleObject#toString()
+     */
+    public String toString() {
+        if (string is null) {
+            string = Format( "Category({},{},{},{})", id, name, description, defined );
+        }
+        return string;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.common.HandleObject#undefine()
+     */
+    public void undefine() {
+        string = null;
+
+        final bool definedChanged = defined;
+        defined = false;
+
+        final bool nameChanged = name !is null;
+        name = null;
+
+        final bool descriptionChanged = description !is null;
+        description = null;
+
+        fireCategoryChanged(new CategoryEvent(this, definedChanged,
+                descriptionChanged, nameChanged));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/CategoryEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.CategoryEvent;
+
+import dwtx.core.commands.common.AbstractNamedHandleEvent;
+import dwtx.core.commands.Category;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>Category</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICategoryListener#categoryChanged(CategoryEvent)
+ */
+public final class CategoryEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The category that has changed; this value is never <code>null</code>.
+     */
+    private final Category category;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param category
+     *            the instance of the interface that changed.
+     * @param definedChanged
+     *            true, iff the defined property changed.
+     * @param descriptionChanged
+     *            true, iff the description property changed.
+     * @param nameChanged
+     *            true, iff the name property changed.
+     */
+    public this(Category category, bool definedChanged,
+            bool descriptionChanged, bool nameChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (category is null) {
+            throw new NullPointerException();
+        }
+        this.category = category;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Category getCategory() {
+        return category;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/Command.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,1103 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 dwtx.core.commands.Command;
+
+// import java.io.BufferedWriter;
+// import java.io.IOException;
+// import java.io.StringWriter;
+
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.commands.util.Tracing;
+import dwtx.core.internal.commands.util.Util;
+import dwtx.core.runtime.ISafeRunnable;
+import dwtx.core.runtime.ListenerList;
+import dwtx.core.runtime.SafeRunner;
+
+import dwtx.core.commands.IParameter;
+import dwtx.core.commands.IHandlerListener;
+import dwtx.core.commands.NotEnabledException;
+import dwtx.core.commands.NotHandledException;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.CommandEvent;
+import dwtx.core.commands.State;
+import dwtx.core.commands.Category;
+import dwtx.core.commands.NamedHandleObjectWithState;
+import dwtx.core.commands.ExecutionEvent;
+import dwtx.core.commands.ParameterType;
+import dwtx.core.commands.IExecutionListener;
+import dwtx.core.commands.ICommandListener;
+import dwtx.core.commands.IHandler;
+import dwtx.core.commands.IObjectWithState;
+import dwtx.core.commands.IExecutionListenerWithChecks;
+import dwtx.core.commands.ITypedParameter;
+import dwtx.core.commands.HandlerEvent;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * <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 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())) {
+            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() ISafeRunnable {
+                ICommandListener listener_;
+                this(){ this.listener_ = listener; }
+                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(String)} 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;
+        }
+
+        return handler.isEnabled();
+    }
+
+    /**
+     * 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 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
+     */
+    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 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));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/CommandEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 dwtx.core.commands.CommandEvent;
+
+import dwtx.core.commands.common.AbstractNamedHandleEvent;
+import dwtx.core.commands.Command;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>Command</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICommandListener#commandChanged(CommandEvent)
+ */
+public final class CommandEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The bit used to represent whether the command has changed its category.
+     */
+    private static const int CHANGED_CATEGORY = LAST_USED_BIT << 1;
+
+    /**
+     * The bit used to represent whether the command has changed its handler.
+     */
+    private static const int CHANGED_HANDLED = LAST_USED_BIT << 2;
+
+    /**
+     * The bit used to represent whether the command has changed its parameters.
+     */
+    private static const int CHANGED_PARAMETERS = LAST_USED_BIT << 3;
+
+    /**
+     * The bit used to represent whether the command has changed its return
+     * type.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_RETURN_TYPE = LAST_USED_BIT << 4;
+
+    /**
+     * The bit used to represent whether the command has changed its help
+     * context identifier.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_HELP_CONTEXT_ID = LAST_USED_BIT << 5;
+
+    /**
+     * The bit used to represent whether this commands active handler has
+     * changed its enablement state.
+     *
+     * @since 3.3
+     */
+    private static const int CHANGED_ENABLED = LAST_USED_BIT << 6;
+
+    /**
+     * The command that has changed; this value is never <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged,
+                returnTypeChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @param helpContextIdChanged
+     *            <code>true</code> iff the help context identifier changed;
+     *            <code>false</code> otherwise.
+     * @since 3.2
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged,
+            bool helpContextIdChanged) {
+        this(command, categoryChanged, definedChanged, descriptionChanged,
+                handledChanged, nameChanged, parametersChanged,
+                returnTypeChanged, helpContextIdChanged, false);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param command
+     *            the instance of the interface that changed.
+     * @param categoryChanged
+     *            <code>true</code>, iff the category property changed.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param handledChanged
+     *            <code>true</code>, iff the handled property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param parametersChanged
+     *            <code>true</code> if the parameters have changed;
+     *            <code>false</code> otherwise.
+     * @param returnTypeChanged
+     *            <code>true</code> iff the return type property changed;
+     *            <code>false</code> otherwise.
+     * @param helpContextIdChanged
+     *            <code>true</code> iff the help context identifier changed;
+     *            <code>false</code> otherwise.
+     * @param enabledChanged
+     *            <code>true</code> iff the comand enablement changed;
+     *            <code>false</code> otherwise.
+     * @since 3.3
+     */
+    public this(Command command, bool categoryChanged,
+            bool definedChanged, bool descriptionChanged,
+            bool handledChanged, bool nameChanged,
+            bool parametersChanged, bool returnTypeChanged,
+            bool helpContextIdChanged, bool enabledChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (command is null) {
+            throw new NullPointerException();
+        }
+        this.command = command;
+
+        if (categoryChanged) {
+            changedValues |= CHANGED_CATEGORY;
+        }
+        if (handledChanged) {
+            changedValues |= CHANGED_HANDLED;
+        }
+        if (parametersChanged) {
+            changedValues |= CHANGED_PARAMETERS;
+        }
+        if (returnTypeChanged) {
+            changedValues |= CHANGED_RETURN_TYPE;
+        }
+        if (helpContextIdChanged) {
+            changedValues |= CHANGED_HELP_CONTEXT_ID;
+        }
+        if (enabledChanged) {
+            changedValues |= CHANGED_ENABLED;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns whether or not the category property changed.
+     *
+     * @return <code>true</code>, iff the category property changed.
+     */
+    public final bool isCategoryChanged() {
+        return ((changedValues & CHANGED_CATEGORY) !is 0);
+    }
+
+    /**
+     * Returns whether or not the handled property changed.
+     *
+     * @return <code>true</code>, iff the handled property changed.
+     */
+    public final bool isHandledChanged() {
+        return ((changedValues & CHANGED_HANDLED) !is 0);
+    }
+
+    /**
+     * Returns whether or not the help context identifier changed.
+     *
+     * @return <code>true</code>, iff the help context identifier changed.
+     * @since 3.2
+     */
+    public final bool isHelpContextIdChanged() {
+        return ((changedValues & CHANGED_HELP_CONTEXT_ID) !is 0);
+    }
+
+    /**
+     * Returns whether or not the parameters have changed.
+     *
+     * @return <code>true</code>, iff the parameters property changed.
+     */
+    public final bool isParametersChanged() {
+        return ((changedValues & CHANGED_PARAMETERS) !is 0);
+    }
+
+    /**
+     * Returns whether or not the return type property changed.
+     *
+     * @return <code>true</code>, iff the return type property changed.
+     * @since 3.2
+     */
+    public final bool isReturnTypeChanged() {
+        return ((changedValues & CHANGED_RETURN_TYPE) !is 0);
+    }
+
+    /**
+     * Return whether the enable property changed.
+     *
+     * @return <code>true</code> iff the comand enablement changed
+     * @since 3.3
+     */
+    public final bool isEnabledChanged() {
+        return ((changedValues & CHANGED_ENABLED) !is 0);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/CommandManager.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,989 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.CommandManager;
+
+// import java.util.ArrayList;
+// import java.util.Collections;
+// import java.util.HashMap;
+// import java.util.HashSet;
+// import java.util.Iterator;
+// import java.util.Map;
+// import java.util.Set;
+// import java.util.WeakHashMap;
+import tango.util.collection.HashMap;
+import tango.util.collection.HashSet;
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Map;
+import tango.util.collection.model.Set;
+import tango.util.collection.model.SetView;
+
+import dwtx.core.commands.common.HandleObjectManager;
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.runtime.ListenerList;
+
+import dwtx.core.commands.ICategoryListener;
+import dwtx.core.commands.ICommandListener;
+import dwtx.core.commands.IExecutionListener;
+import dwtx.core.commands.IExecutionListenerWithChecks;
+import dwtx.core.commands.IParameterTypeListener;
+import dwtx.core.commands.ICommandManagerListener;
+import dwtx.core.commands.CommandEvent;
+import dwtx.core.commands.CommandManagerEvent;
+import dwtx.core.commands.Command;
+import dwtx.core.commands.Category;
+import dwtx.core.commands.CategoryEvent;
+import dwtx.core.commands.IHandler;
+import dwtx.core.commands.ParameterType;
+import dwtx.core.commands.IParameter;
+import dwtx.core.commands.Parameterization;
+import dwtx.core.commands.ParameterizedCommand;
+import dwtx.core.commands.ParameterTypeEvent;
+import dwtx.core.commands.SerializationException;
+
+import dwtx.core.commands.NotEnabledException;
+import dwtx.core.commands.NotHandledException;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.ExecutionEvent;
+
+import dwt.dwthelper.utils;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * A central repository for commands -- both in the defined and undefined
+ * states. Commands can be created and retrieved using this manager. It is
+ * possible to listen to changes in the collection of commands by attaching a
+ * listener to the manager.
+ * </p>
+ *
+ * @see CommandManager#getCommand(String)
+ * @since 3.1
+ */
+public final class CommandManager : HandleObjectManager,
+        ICategoryListener, ICommandListener, IParameterTypeListener {
+
+    /**
+     * A listener that forwards incoming execution events to execution listeners
+     * on this manager. The execution events will come from any command on this
+     * manager.
+     *
+     * @since 3.1
+     */
+    private final class ExecutionListener :
+            IExecutionListenerWithChecks {
+
+        public void notDefined(String commandId, NotDefinedException exception) {
+            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(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public void notEnabled(String commandId, NotEnabledException exception) {
+            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(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void notHandled(String commandId,
+                NotHandledException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.notHandled(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void postExecuteFailure(String commandId,
+                ExecutionException exception) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.postExecuteFailure(commandId, exception);
+                    }
+                }
+            }
+        }
+
+        public final void postExecuteSuccess(String commandId,
+                Object returnValue) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.postExecuteSuccess(commandId, returnValue);
+                    }
+                }
+            }
+        }
+
+        public final void preExecute(String commandId,
+                ExecutionEvent event) {
+            if (executionListeners !is null) {
+                Object[] listeners = executionListeners.getListeners();
+                for (int i = 0; i < listeners.length; i++) {
+                    Object object = listeners[i];
+                    if ( auto listener = cast(IExecutionListener)object ) {
+                        listener.preExecute(commandId, event);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The identifier of the category in which all auto-generated commands will
+     * appear. This value must never be <code>null</code>.
+     *
+     * @since 3.2
+     */
+    public static const String AUTOGENERATED_CATEGORY_ID = "dwtx.core.commands.categories.autogenerated"; //$NON-NLS-1$
+
+    /**
+     * The escape character to use for serialization and deserialization of
+     * parameterized commands.
+     */
+    static const char ESCAPE_CHAR = '%';
+
+    /**
+     * The character that separates a parameter id from its value.
+     */
+    static const char ID_VALUE_CHAR = '=';
+
+    /**
+     * The character that indicates the end of a list of parameters.
+     */
+    static const char PARAMETER_END_CHAR = ')';
+
+    /**
+     * The character that separators parameters from each other.
+     */
+    static const char PARAMETER_SEPARATOR_CHAR = ',';
+
+    /**
+     * The character that indicates the start of a list of parameters.
+     */
+    static const char PARAMETER_START_CHAR = '(';
+
+    this(){
+        categoriesById = new HashMap!(String,Category);
+        definedCategoryIds = new HashSet!(String);
+        definedParameterTypeIds = new HashSet!(String);
+        parameterTypesById = new HashMap!(String,ParameterType);
+        helpContextIdsByHandler = new WeakHashMap!(IHandler,String);
+    }
+
+    /**
+     * Unescapes special characters in the command id, parameter ids and
+     * parameter values for {@link #deserialize()}. The special characters
+     * {@link #PARAMETER_START_CHAR}, {@link #PARAMETER_END_CHAR},
+     * {@link #ID_VALUE_CHAR}, {@link #PARAMETER_SEPARATOR_CHAR} and
+     * {@link #ESCAPE_CHAR} are escaped by prepending an {@link #ESCAPE_CHAR}
+     * character.
+     *
+     * @param escapedText
+     *            a <code>String</code> that may contain escaped special
+     *            characters for command serialization.
+     * @return a <code>String</code> representing <code>escapedText</code>
+     *         with any escaped characters replaced by their literal values
+     * @throws SerializationException
+     *             if <code>escapedText</code> contains an invalid escape
+     *             sequence
+     * @see ParameterizedCommand#escape(String)
+     * @since 3.2
+     */
+    private static final String unescape(String escapedText) {
+
+        // defer initialization of a StringBuffer until we know we need one
+        StringBuffer buffer;
+
+        for (int i = 0; i < escapedText.length; i++) {
+
+            char c = escapedText.charAt(i);
+            if (c !is ESCAPE_CHAR) {
+                // normal unescaped character
+                if (buffer !is null) {
+                    buffer.append(c);
+                }
+            } else {
+                if (buffer is null) {
+                    buffer = new StringBuffer(escapedText.substring(0, i));
+                }
+
+                if (++i < escapedText.length) {
+                    c = escapedText.charAt(i);
+                    switch (c) {
+                    case PARAMETER_START_CHAR:
+                    case PARAMETER_END_CHAR:
+                    case ID_VALUE_CHAR:
+                    case PARAMETER_SEPARATOR_CHAR:
+                    case ESCAPE_CHAR:
+                        buffer.append(c);
+                        break;
+                    default:
+                        throw new SerializationException(
+                                "Invalid character '" ~ c ~ "' in escape sequence"); //$NON-NLS-1$ //$NON-NLS-2$
+                    }
+                } else {
+                    throw new SerializationException(
+                            "Unexpected termination of escape sequence"); //$NON-NLS-1$
+                }
+            }
+
+        }
+
+        if (buffer is null) {
+            return escapedText;
+        }
+
+        return buffer.toString();
+    }
+
+    /**
+     * The map of category identifiers (<code>String</code>) to categories (
+     * <code>Category</code>). This collection may be empty, but it is never
+     * <code>null</code>.
+     */
+    private const Map!(String,Category) categoriesById;
+
+    /**
+     * The set of identifiers for those categories that are defined. This value
+     * may be empty, but it is never <code>null</code>.
+     */
+    private const Set!(String) definedCategoryIds;
+
+    /**
+     * The set of identifiers for those command parameter types that are
+     * defined. This value may be empty, but it is never <code>null</code>.
+     *
+     * @since 3.2
+     */
+    private const Set!(String) definedParameterTypeIds;
+
+    /**
+     * The execution listener for this command manager. This just forwards
+     * events from commands controlled by this manager to listeners on this
+     * manager.
+     */
+    private IExecutionListener executionListener = null;
+
+    /**
+     * The collection of execution listeners. This collection is
+     * <code>null</code> if there are no listeners.
+     */
+    private ListenerList executionListeners = null;
+
+    /**
+     * The help context identifiers ({@link String}) for a handler ({@link IHandler}).
+     * This map may be empty, but it is never <code>null</code>. Entries are
+     * removed if all strong references to the handler are removed.
+     *
+     * @since 3.2
+     */
+    private const WeakHashMap!(IHandler,String) helpContextIdsByHandler;
+
+    /**
+     * The map of parameter type identifiers (<code>String</code>) to
+     * parameter types ( <code>ParameterType</code>). This collection may be
+     * empty, but it is never <code>null</code>.
+     *
+     * @since 3.2
+     */
+    private const Map!(String,ParameterType) parameterTypesById;
+
+    /**
+     * Adds a listener to this command manager. The listener will be notified
+     * when the set of defined commands changes. This can be used to track the
+     * global appearance and disappearance of commands.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addCommandManagerListener(
+            ICommandManagerListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Adds an execution listener to this manager. This listener will be
+     * notified if any of the commands controlled by this manager execute. This
+     * can be used to support macros and instrumentation of commands.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addExecutionListener(IExecutionListener listener) {
+        if (listener is null) {
+            throw new NullPointerException(
+                    "Cannot add a null execution listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            executionListeners = new ListenerList(ListenerList.IDENTITY);
+
+            // Add an execution listener to every command.
+            executionListener = new ExecutionListener();
+            foreach( k, v; handleObjectsById ){
+                Command command = cast(Command) v;
+                command.addExecutionListener(executionListener);
+            }
+
+        }
+
+        executionListeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.ICategoryListener#categoryChanged(dwtx.core.commands.CategoryEvent)
+     */
+    public final void categoryChanged(CategoryEvent categoryEvent) {
+        if (categoryEvent.isDefinedChanged()) {
+            Category category = categoryEvent.getCategory();
+            String categoryId = category.getId();
+            bool categoryIdAdded = category.isDefined();
+            if (categoryIdAdded) {
+                definedCategoryIds.add(categoryId);
+            } else {
+                definedCategoryIds.remove(categoryId);
+            }
+            if (isListenerAttached()) {
+                fireCommandManagerChanged(new CommandManagerEvent(this, null,
+                        false, false, categoryId, categoryIdAdded, true));
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.commands.ICommandListener#commandChanged(dwtx.commands.CommandEvent)
+     */
+    public final void commandChanged(CommandEvent commandEvent) {
+        if (commandEvent.isDefinedChanged()) {
+            Command command = commandEvent.getCommand();
+            String commandId = command.getId();
+            bool commandIdAdded = command.isDefined();
+            if (commandIdAdded) {
+                definedHandleObjects.add(command);
+            } else {
+                definedHandleObjects.remove(command);
+            }
+            if (isListenerAttached()) {
+                fireCommandManagerChanged(new CommandManagerEvent(this,
+                        commandId, commandIdAdded, true, null, false, false));
+            }
+        }
+    }
+
+    /**
+     * Sets the name and description of the category for uncategorized commands.
+     * This is the category that will be returned if
+     * {@link #getCategory(String)} is called with <code>null</code>.
+     *
+     * @param name
+     *            The name of the category for uncategorized commands; must not
+     *            be <code>null</code>.
+     * @param description
+     *            The description of the category for uncategorized commands;
+     *            may be <code>null</code>.
+     * @since 3.2
+     */
+    public final void defineUncategorizedCategory(String name,
+            String description) {
+        Category category = getCategory(AUTOGENERATED_CATEGORY_ID);
+        category.define(name, description);
+    }
+
+    /**
+     * <p>
+     * Returns a {@link ParameterizedCommand} with a command and
+     * parameterizations as specified in the provided
+     * <code>serializedParameterizedCommand</code> string. The
+     * <code>serializedParameterizedCommand</code> must use the format
+     * returned by {@link ParameterizedCommand#serialize()} and described in the
+     * Javadoc for that method.
+     * </p>
+     * <p>
+     * If a parameter id encoded in the
+     * <code>serializedParameterizedCommand</code> does not exist in the
+     * encoded command, that parameter id and value are ignored. A given
+     * parameter id should not be used more than once in
+     * <code>serializedParameterizedCommand</code>. This will not result in
+     * an exception, but in this case the value of the parameter when the
+     * command is executed is unspecified.
+     * </p>
+     * <p>
+     * This method will never return <code>null</code>, however it may throw
+     * an exception if there is a problem processing the serialization string or
+     * the encoded command is undefined.
+     * </p>
+     *
+     * @param serializedParameterizedCommand
+     *            a string representing a command id and parameter ids and
+     *            values; must not be <code>null</code>
+     * @return a {@link ParameterizedCommand} with the command and
+     *         parameterizations encoded in the
+     *         <code>serializedParameterizedCommand</code>; never
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             if the command indicated in
+     *             <code>serializedParameterizedCommand</code> is not defined
+     * @throws SerializationException
+     *             if there is an error deserializing
+     *             <code>serializedParameterizedCommand</code>
+     * @see ParameterizedCommand#serialize()
+     * @since 3.2
+     */
+    public final ParameterizedCommand deserialize(
+            String serializedParameterizedCommand) {
+
+        int lparenPosition = unescapedIndexOf(
+                serializedParameterizedCommand, PARAMETER_START_CHAR);
+
+        String commandIdEscaped;
+        String serializedParameters;
+        if (lparenPosition is -1) {
+            commandIdEscaped = serializedParameterizedCommand;
+            serializedParameters = null;
+        } else {
+            commandIdEscaped = serializedParameterizedCommand.substring(0,
+                    lparenPosition);
+
+            if (serializedParameterizedCommand
+                    .charAt(serializedParameterizedCommand.length - 1) !is PARAMETER_END_CHAR) {
+                throw new SerializationException(
+                        "Parentheses must be balanced in serialized ParameterizedCommand"); //$NON-NLS-1$
+            }
+
+            serializedParameters = serializedParameterizedCommand.substring(
+                    lparenPosition + 1, // skip PARAMETER_START_CHAR
+                    serializedParameterizedCommand.length - 1); // skip
+            // PARAMETER_END_CHAR
+        }
+
+        String commandId = unescape(commandIdEscaped);
+        Command command = getCommand(commandId);
+        IParameter[] parameters = command.getParameters();
+        Parameterization[] parameterizations = getParameterizations(
+                serializedParameters, parameters);
+
+        return new ParameterizedCommand(command, parameterizations);
+    }
+
+    /**
+     * Notifies all of the listeners to this manager that the set of defined
+     * command identifiers has changed.
+     *
+     * @param event
+     *            The event to send to all of the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireCommandManagerChanged(CommandManagerEvent event) {
+        if (event is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            ICommandManagerListener listener = cast(ICommandManagerListener) listeners[i];
+            listener.commandManagerChanged(event);
+        }
+    }
+
+    /**
+     * Returns all of the commands known by this manager -- defined and
+     * undefined.
+     *
+     * @return All of the commands; may be empty, but never <code>null</code>.
+     * @since 3.2
+     */
+    public final Command[] getAllCommands() {
+        Command[] res;
+        res.length = handleObjectsById.size();
+        int idx;
+        foreach( cmd; handleObjectsById.elements() ){
+            res[idx] = cast(Command)cmd;
+            idx++;
+        }
+        return res;
+    }
+
+    /**
+     * Gets the category with the given identifier. If no such category
+     * currently exists, then the category will be created (but be undefined).
+     *
+     * @param categoryId
+     *            The identifier to find; must not be <code>null</code>. If
+     *            the category is <code>null</code>, then a category suitable
+     *            for uncategorized items is defined and returned.
+     * @return The category with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Category
+     */
+    public final Category getCategory(String categoryId) {
+        if (categoryId is null) {
+            return getCategory(AUTOGENERATED_CATEGORY_ID);
+        }
+
+        checkId(categoryId);
+
+        Category category = cast(Category) categoriesById.get(categoryId);
+        if (category is null) {
+            category = new Category(categoryId);
+            categoriesById.add(categoryId, category);
+            category.addCategoryListener(this);
+        }
+
+        return category;
+    }
+
+    /**
+     * Gets the command with the given identifier. If no such command currently
+     * exists, then the command will be created (but will be undefined).
+     *
+     * @param commandId
+     *            The identifier to find; must not be <code>null</code> and
+     *            must not be zero-length.
+     * @return The command with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Command
+     */
+    public final Command getCommand(String commandId) {
+        checkId(commandId);
+
+        Command command = cast(Command) handleObjectsById.get(commandId);
+        if (command is null) {
+            command = new Command(commandId);
+            handleObjectsById.add(commandId, command);
+            command.addCommandListener(this);
+
+            if (executionListener !is null) {
+                command.addExecutionListener(executionListener);
+            }
+        }
+
+        return command;
+    }
+
+    /**
+     * Returns the categories that are defined.
+     *
+     * @return The defined categories; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Category[] getDefinedCategories() {
+        Category[] categories = new Category[definedCategoryIds.size()];
+//         Iterator categoryIdItr = definedCategoryIds.iterator();
+        int i = 0;
+        foreach( categoryId; definedCategoryIds ){
+//         while (categoryIdItr.hasNext()) {
+//             String categoryId = cast(String) categoryIdItr.next();
+            categories[i++] = getCategory(categoryId);
+        }
+        return categories;
+    }
+
+    /**
+     * Returns the set of identifiers for those category that are defined.
+     *
+     * @return The set of defined category identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final SetView!(String) getDefinedCategoryIds() {
+        return definedCategoryIds;
+    }
+
+    /**
+     * Returns the set of identifiers for those commands that are defined.
+     *
+     * @return The set of defined command identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final SetView!(String) getDefinedCommandIds() {
+        return getDefinedHandleObjectIds();
+    }
+
+    /**
+     * Returns the commands that are defined.
+     *
+     * @return The defined commands; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Command[] getDefinedCommands() {
+        return cast(Command[]) definedHandleObjects
+                .toArray();
+    }
+
+    /**
+     * Returns the set of identifiers for those parameter types that are
+     * defined.
+     *
+     * @return The set of defined command parameter type identifiers; this value
+     *         may be empty, but it is never <code>null</code>.
+     * @since 3.2
+     */
+    public final SetView!(String) getDefinedParameterTypeIds() {
+        return definedParameterTypeIds;
+    }
+
+    /**
+     * Returns the command parameter types that are defined.
+     *
+     * @return The defined command parameter types; this value may be empty, but
+     *         it is never <code>null</code>.
+     * @since 3.2
+     */
+    public final ParameterType[] getDefinedParameterTypes() {
+        ParameterType[] parameterTypes = new ParameterType[definedParameterTypeIds
+                .size()];
+//         Iterator iterator = definedParameterTypeIds.iterator();
+        int i = 0;
+        foreach( parameterTypeId; definedParameterTypeIds ){
+//         while (iterator.hasNext()) {
+//             String parameterTypeId = cast(String) iterator.next();
+            parameterTypes[i++] = getParameterType(parameterTypeId);
+        }
+        return parameterTypes;
+    }
+
+    /**
+     * Gets the help context identifier for a particular command. The command's
+     * handler is first checked for a help context identifier. If the handler
+     * does not have a help context identifier, then the help context identifier
+     * for the command is returned. If neither has a help context identifier,
+     * then <code>null</code> is returned.
+     *
+     * @param command
+     *            The command for which the help context should be retrieved;
+     *            must not be <code>null</code>.
+     * @return The help context identifier to use for the given command; may be
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             If the given command is not defined.
+     * @since 3.2
+     */
+    public final String getHelpContextId(Command command) {
+        // Check if the command is defined.
+        if (!command.isDefined()) {
+            throw new NotDefinedException("The command is not defined. " //$NON-NLS-1$
+                    ~ command.getId());
+        }
+
+        // Check the handler.
+        IHandler handler = command.getHandler();
+        if (handler !is null) {
+            String helpContextId = cast(String) helpContextIdsByHandler
+                    .get(handler);
+            if (helpContextId !is null) {
+                return helpContextId;
+            }
+        }
+
+        // Simply return whatever the command has as a help context identifier.
+        return command.getHelpContextId();
+    }
+
+    /**
+     * Returns an array of parameterizations for the provided command by
+     * deriving the parameter ids and values from the provided
+     * <code>serializedParameters</code> string.
+     *
+     * @param serializedParameters
+     *            a String encoding parameter ids and values; must not be
+     *            <code>null</code>.
+     * @param parameters
+     *            array of parameters of the command being deserialized; may be
+     *            <code>null</code>.
+     * @return an array of parameterizations; may be <code>null</code>.
+     * @throws NotDefinedException
+     *             if the command is not defined
+     * @throws SerializationException
+     *             if there is an error deserializing the parameters
+     * @since 3.2
+     */
+    private final Parameterization[] getParameterizations(
+            String serializedParameters, IParameter[] parameters) {
+
+        if (serializedParameters is null
+                || (serializedParameters.length is 0)) {
+            return null;
+        }
+
+        if ((parameters is null) || (parameters.length is 0)) {
+            return null;
+        }
+
+        auto paramList = new ArraySeq!(Parameterization);
+
+        int commaPosition; // split off each param by looking for ','
+        do {
+            commaPosition = unescapedIndexOf(serializedParameters, ',');
+
+            String idEqualsValue;
+            if (commaPosition is -1) {
+                // no more parameters after this
+                idEqualsValue = serializedParameters;
+            } else {
+                // take the first parameter...
+                idEqualsValue = serializedParameters
+                        .substring(0, commaPosition);
+
+                // ... and put the rest back into serializedParameters
+                serializedParameters = serializedParameters
+                        .substring(commaPosition + 1);
+            }
+
+            int equalsPosition = unescapedIndexOf(idEqualsValue, '=');
+
+            String parameterId;
+            String parameterValue;
+            if (equalsPosition is -1) {
+                // missing values are null
+                parameterId = unescape(idEqualsValue);
+                parameterValue = null;
+            } else {
+                parameterId = unescape(idEqualsValue.substring(0,
+                        equalsPosition));
+                parameterValue = unescape(idEqualsValue
+                        .substring(equalsPosition + 1));
+            }
+
+            for (int i = 0; i < parameters.length; i++) {
+                IParameter parameter = parameters[i];
+                if (parameter.getId().equals(parameterId)) {
+                    paramList.append(new Parameterization(parameter,
+                            parameterValue));
+                    break;
+                }
+            }
+
+        } while (commaPosition !is -1);
+
+        return cast(Parameterization[]) paramList
+                .toArray();
+    }
+
+    /**
+     * Gets the command {@link ParameterType} with the given identifier. If no
+     * such command parameter type currently exists, then the command parameter
+     * type will be created (but will be undefined).
+     *
+     * @param parameterTypeId
+     *            The identifier to find; must not be <code>null</code> and
+     *            must not be zero-length.
+     * @return The {@link ParameterType} with the given identifier; this value
+     *         will never be <code>null</code>, but it might be undefined.
+     * @since 3.2
+     */
+    public final ParameterType getParameterType(String parameterTypeId) {
+        checkId(parameterTypeId);
+
+        ParameterType parameterType = cast(ParameterType) parameterTypesById
+                .get(parameterTypeId);
+        if (parameterType is null) {
+            parameterType = new ParameterType(parameterTypeId);
+            parameterTypesById.add(parameterTypeId, parameterType);
+            parameterType.addListener(this);
+        }
+
+        return parameterType;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.2
+     */
+    public final void parameterTypeChanged(
+            ParameterTypeEvent parameterTypeEvent) {
+        if (parameterTypeEvent.isDefinedChanged()) {
+            ParameterType parameterType = parameterTypeEvent
+                    .getParameterType();
+            String parameterTypeId = parameterType.getId();
+            bool parameterTypeIdAdded = parameterType.isDefined();
+            if (parameterTypeIdAdded) {
+                definedParameterTypeIds.add(parameterTypeId);
+            } else {
+                definedParameterTypeIds.remove(parameterTypeId);
+            }
+
+            fireCommandManagerChanged(new CommandManagerEvent(this,
+                    parameterTypeId, parameterTypeIdAdded, true));
+        }
+    }
+
+    /**
+     * Removes a listener from this command manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeCommandManagerListener(
+            ICommandManagerListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Removes an execution listener from this command manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeExecutionListener(IExecutionListener listener) {
+        if (listener is null) {
+            throw new NullPointerException("Cannot remove a null listener"); //$NON-NLS-1$
+        }
+
+        if (executionListeners is null) {
+            return;
+        }
+
+        executionListeners.remove(cast(Object)listener);
+
+        if (executionListeners.isEmpty()) {
+            executionListeners = null;
+
+            // Remove the execution listener to every command.
+            foreach( k, v; handleObjectsById ){
+//             Iterator commandItr = handleObjectsById.values().iterator();
+//             while (commandItr.hasNext()) {
+                Command command = cast(Command) v;
+                command.removeExecutionListener(executionListener);
+            }
+            executionListener = null;
+
+        }
+    }
+
+    /**
+     * Block updates all of the handlers for all of the commands. If the handler
+     * is <code>null</code> or the command id does not exist in the map, then
+     * the command becomes unhandled. Otherwise, the handler is set to the
+     * corresponding value in the map.
+     *
+     * @param handlersByCommandId
+     *            A map of command identifiers (<code>String</code>) to
+     *            handlers (<code>IHandler</code>). This map may be
+     *            <code>null</code> if all handlers should be cleared.
+     *            Similarly, if the map is empty, then all commands will become
+     *            unhandled.
+     */
+    public final void setHandlersByCommandId(Map!(String,Object) handlersByCommandId) {
+        // Make that all the reference commands are created.
+        foreach( k, v; handlersByCommandId ){
+        //Iterator commandIdItr = handlersByCommandId.keySet().iterator();
+        //while (commandIdItr.hasNext()) {
+            getCommand(k);
+        }
+
+        // Now, set-up the handlers on all of the existing commands.
+//         Iterator commandItr = handleObjectsById.values().iterator();
+        foreach( k, v; handlersByCommandId ){
+//         while (commandItr.hasNext()) {
+            Command command = cast(Command) v;
+            String commandId = command.getId();
+            Object value = handlersByCommandId.get(commandId);
+            if ( auto handler = cast(IHandler) value ) {
+                command.setHandler(handler);
+            } else {
+                command.setHandler(null);
+            }
+        }
+    }
+
+    /**
+     * Sets the help context identifier to associate with a particular handler.
+     *
+     * @param handler
+     *            The handler with which to register a help context identifier;
+     *            must not be <code>null</code>.
+     * @param helpContextId
+     *            The help context identifier to register; may be
+     *            <code>null</code> if the help context identifier should be
+     *            removed.
+     * @since 3.2
+     */
+    public final void setHelpContextId(IHandler handler,
+            String helpContextId) {
+        if (handler is null) {
+            throw new NullPointerException("The handler cannot be null"); //$NON-NLS-1$
+        }
+        if (helpContextId is null) {
+            helpContextIdsByHandler.removeKey(handler);
+        } else {
+            helpContextIdsByHandler.add(handler, helpContextId);
+        }
+    }
+
+    /**
+     * Searches for the index of a <code>char</code> in a <code>String</code>
+     * but disregards characters prefixed with the {@link #ESCAPE_CHAR} escape
+     * character. This is used by {@link #deserialize(String)} and
+     * {@link #getParameterizations(String, IParameter[])} to parse the
+     * serialized parameterized command string.
+     *
+     * @param escapedText
+     *            the string to search for the index of <code>ch</code> in
+     * @param ch
+     *            a character to search for in <code>escapedText</code>
+     * @return the index of the first unescaped occurrence of the character in
+     *         <code>escapedText</code>, or <code>-1</code> if the
+     *         character does not occur unescaped.
+     * @see String#indexOf(int)
+     */
+    private final int unescapedIndexOf(String escapedText, char ch) {
+
+        int pos = escapedText.indexOf(ch);
+
+        // first char can't be escaped
+        if (pos is 0) {
+            return pos;
+        }
+
+        while (pos !is -1) {
+            // look back for the escape character
+            if (escapedText.charAt(pos - 1) !is ESCAPE_CHAR) {
+                return pos;
+            }
+
+            // scan for the next instance of ch
+            pos = escapedText.indexOf(ch, pos + 1);
+        }
+
+        return pos;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/CommandManagerEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,310 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.CommandManagerEvent;
+
+import dwtx.core.commands.CommandManager;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An event indicating that the set of defined command identifiers has changed.
+ * </p>
+ *
+ * @since 3.1
+ * @see ICommandManagerListener#commandManagerChanged(CommandManagerEvent)
+ */
+public final class CommandManagerEvent {
+
+    /**
+     * The bit used to represent whether the given category has become defined.
+     * If this bit is not set and there is no category id, then no category has
+     * become defined nor undefined. If this bit is not set and there is a
+     * category id, then the category has become undefined.
+     */
+    private static const int CHANGED_CATEGORY_DEFINED = 1;
+
+    /**
+     * The bit used to represent whether the given command has become defined.
+     * If this bit is not set and there is no command id, then no command has
+     * become defined nor undefined. If this bit is not set and there is a
+     * command id, then the command has become undefined.
+     */
+    private static const int CHANGED_COMMAND_DEFINED = 1 << 1;
+
+    /**
+     * The bit used to represent whether the given command parameter type has
+     * become defined. If this bit is not set and there is no parameter type id,
+     * then no parameter type has become defined nor undefined. If this bit is
+     * not set and there is a parameter type id, then the parameter type has
+     * become undefined.
+     *
+     * @since 3.2
+     */
+    private static const int CHANGED_PARAMETER_TYPE_DEFINED = 1 << 2;
+
+    /**
+     * The category identifier that was added or removed from the list of
+     * defined category identifiers. This value is <code>null</code> if the
+     * list of defined category identifiers did not change.
+     */
+    private const String categoryId;
+
+    /**
+     * A collection of bits representing whether certain values have changed. A
+     * bit is set (i.e., <code>1</code>) if the corresponding property has
+     * changed.
+     */
+    private const int changedValues;
+
+    /**
+     * The command identifier that was added or removed from the list of defined
+     * command identifiers. This value is <code>null</code> if the list of
+     * defined command identifiers did not change.
+     */
+    private const String commandId;
+
+    /**
+     * The command parameter type identifier that was added or removed from the
+     * list of defined parameter type identifiers. This value is
+     * <code>null</code> if the list of defined parameter type identifiers did
+     * not change.
+     *
+     * @since 3.2
+     */
+    private const String parameterTypeId;
+
+    /**
+     * The command manager that has changed.
+     */
+    private const CommandManager commandManager;
+
+    /**
+     * Creates a new <code>CommandManagerEvent</code> instance to describe
+     * changes to commands and/or categories.
+     *
+     * @param commandManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param commandId
+     *            The command identifier that was added or removed; must not be
+     *            <code>null</code> if commandIdChanged is <code>true</code>.
+     * @param commandIdAdded
+     *            Whether the command identifier became defined (otherwise, it
+     *            became undefined).
+     * @param commandIdChanged
+     *            Whether the list of defined command identifiers has changed.
+     * @param categoryId
+     *            The category identifier that was added or removed; must not be
+     *            <code>null</code> if categoryIdChanged is <code>true</code>.
+     * @param categoryIdAdded
+     *            Whether the category identifier became defined (otherwise, it
+     *            became undefined).
+     * @param categoryIdChanged
+     *            Whether the list of defined category identifiers has changed.
+     */
+    public this(CommandManager commandManager,
+            String commandId, bool commandIdAdded,
+            bool commandIdChanged, String categoryId,
+            bool categoryIdAdded, bool categoryIdChanged) {
+        if (commandManager is null) {
+            throw new NullPointerException(
+                    "An event must refer to its command manager"); //$NON-NLS-1$
+        }
+
+        if (commandIdChanged && (commandId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined commands changed, then the added/removed command must be mentioned"); //$NON-NLS-1$
+        }
+
+        if (categoryIdChanged && (categoryId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined categories changed, then the added/removed category must be mentioned"); //$NON-NLS-1$
+        }
+
+        this.commandManager = commandManager;
+        this.commandId = commandId;
+        this.categoryId = categoryId;
+
+        // this constructor only works for changes to commands and categories
+        this.parameterTypeId = null;
+
+        int changedValues = 0;
+        if (categoryIdChanged && categoryIdAdded) {
+            changedValues |= CHANGED_CATEGORY_DEFINED;
+        }
+        if (commandIdChanged && commandIdAdded) {
+            changedValues |= CHANGED_COMMAND_DEFINED;
+        }
+        this.changedValues = changedValues;
+    }
+
+    /**
+     * Creates a new <code>CommandManagerEvent</code> instance to describe
+     * changes to command parameter types.
+     *
+     * @param commandManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param parameterTypeId
+     *            The command parameter type identifier that was added or
+     *            removed; must not be <code>null</code> if
+     *            parameterTypeIdChanged is <code>true</code>.
+     * @param parameterTypeIdAdded
+     *            Whether the parameter type identifier became defined
+     *            (otherwise, it became undefined).
+     * @param parameterTypeIdChanged
+     *            Whether the list of defined parameter type identifiers has
+     *            changed.
+     *
+     * @since 3.2
+     */
+    public this(CommandManager commandManager,
+            String parameterTypeId, bool parameterTypeIdAdded,
+            bool parameterTypeIdChanged) {
+
+        if (commandManager is null) {
+            throw new NullPointerException(
+                    "An event must refer to its command manager"); //$NON-NLS-1$
+        }
+
+        if (parameterTypeIdChanged && (parameterTypeId is null)) {
+            throw new NullPointerException(
+                    "If the list of defined command parameter types changed, then the added/removed parameter type must be mentioned"); //$NON-NLS-1$
+        }
+
+        this.commandManager = commandManager;
+        this.commandId = null;
+        this.categoryId = null;
+
+        this.parameterTypeId = parameterTypeId;
+
+        int changedValues = 0;
+        if (parameterTypeIdChanged && parameterTypeIdAdded) {
+            changedValues |= CHANGED_PARAMETER_TYPE_DEFINED;
+        }
+
+        this.changedValues = changedValues;
+    }
+
+    /**
+     * Returns the category identifier that was added or removed.
+     *
+     * @return The category identifier that was added or removed; may be
+     *         <code>null</code>.
+     */
+    public final String getCategoryId() {
+        return categoryId;
+    }
+
+    /**
+     * Returns the command identifier that was added or removed.
+     *
+     * @return The command identifier that was added or removed; may be
+     *         <code>null</code>.
+     */
+    public final String getCommandId() {
+        return commandId;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final CommandManager getCommandManager() {
+        return commandManager;
+    }
+
+    /**
+     * Returns the command parameter type identifier that was added or removed.
+     *
+     * @return The command parameter type identifier that was added or removed;
+     *         may be <code>null</code>.
+     *
+     * @since 3.2
+     */
+    public final String getParameterTypeId() {
+        return parameterTypeId;
+    }
+
+    /**
+     * Returns whether the list of defined category identifiers has changed.
+     *
+     * @return <code>true</code> if the list of category identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isCategoryChanged() {
+        return (categoryId !is null);
+    }
+
+    /**
+     * Returns whether the category identifier became defined. Otherwise, the
+     * category identifier became undefined.
+     *
+     * @return <code>true</code> if the category identifier became defined;
+     *         <code>false</code> if the category identifier became undefined.
+     */
+    public final bool isCategoryDefined() {
+        return (((changedValues & CHANGED_CATEGORY_DEFINED) !is 0) && (categoryId !is null));
+    }
+
+    /**
+     * Returns whether the list of defined command identifiers has changed.
+     *
+     * @return <code>true</code> if the list of command identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isCommandChanged() {
+        return (commandId !is null);
+    }
+
+    /**
+     * Returns whether the command identifier became defined. Otherwise, the
+     * command identifier became undefined.
+     *
+     * @return <code>true</code> if the command identifier became defined;
+     *         <code>false</code> if the command identifier became undefined.
+     */
+    public final bool isCommandDefined() {
+        return (((changedValues & CHANGED_COMMAND_DEFINED) !is 0) && (commandId !is null));
+    }
+
+    /**
+     * Returns whether the list of defined command parameter type identifiers
+     * has changed.
+     *
+     * @return <code>true</code> if the list of command parameter type
+     *         identifiers has changed; <code>false</code> otherwise.
+     *
+     * @since 3.2
+     */
+    public final bool isParameterTypeChanged() {
+        return (parameterTypeId !is null);
+    }
+
+    /**
+     * Returns whether the command parameter type identifier became defined.
+     * Otherwise, the command parameter type identifier became undefined.
+     *
+     * @return <code>true</code> if the command parameter type identifier
+     *         became defined; <code>false</code> if the command parameter
+     *         type identifier became undefined.
+     *
+     * @since 3.2
+     */
+    public final bool isParameterTypeDefined() {
+        return (((changedValues & CHANGED_PARAMETER_TYPE_DEFINED) !is 0) && (parameterTypeId !is null));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ExecutionEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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 dwtx.core.commands.ExecutionEvent;
+
+// import java.util.Collections;
+import tango.util.collection.model.Map;
+import tango.util.collection.HashMap;
+
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.Command;
+import dwtx.core.commands.ParameterType;
+import dwtx.core.commands.AbstractParameterValueConverter;
+import dwtx.core.commands.ParameterValueConversionException;
+
+import dwt.dwthelper.utils;
+
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * The data object to pass to the command (and its handler) as it executes. This
+ * carries information about the current state of the application, and the
+ * application context in which the command was executed.
+ * </p>
+ * <p>
+ * An execution event carries three blocks of data: the parameters, the trigger,
+ * and the application context. How these blocks are used is application
+ * dependent. In the Eclipse workbench, the trigger is an DWT event, and the
+ * application context contains information about the selection and active part.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ExecutionEvent {
+    private static const Map!(String,String) EMPTY_MAP;
+    static this(){
+        EMPTY_MAP = new HashMap!(String,String);
+    }
+    /**
+     * The state of the application at the time the execution was triggered. In
+     * the Eclipse workbench, this might contain information about the active
+     * part of the active selection (for example). This value may be
+     * <code>null</code>.
+     */
+    private const Object applicationContext;
+
+    /**
+     * The command being executed. This value may be <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * The parameters to qualify the execution. For handlers that normally
+     * prompt for additional information, these can be used to avoid prompting.
+     * This value may be empty, but it is never <code>null</code>.
+     */
+    private const Map!(String,String) parameters;
+
+    /**
+     * The object that triggered the execution. In an event-driven architecture,
+     * this is typically just another event. In the Eclipse workbench, this is
+     * typically an DWT event. This value may be <code>null</code>.
+     */
+    private const Object trigger;
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code> with no
+     * parameters, no trigger and no application context. This is just a
+     * convenience method.
+     *
+     * @since 3.2
+     */
+    public this() {
+        this(null, EMPTY_MAP, null, null);
+    }
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code>.
+     *
+     * @param parameters
+     *            The parameters to qualify the execution; must not be
+     *            <code>null</code>. This must be a map of parameter ids (<code>String</code>)
+     *            to parameter values (<code>String</code>).
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @deprecated use
+     *             {@link ExecutionEvent#ExecutionEvent(Command, Map, Object, Object)}
+     */
+    public this(Map!(String,String) parameters, Object trigger,
+            Object applicationContext) {
+        this(null, parameters, trigger, applicationContext);
+    }
+
+    /**
+     * Constructs a new instance of <code>ExecutionEvent</code>.
+     *
+     * @param command
+     *            The command being executed; may be <code>null</code>.
+     * @param parameters
+     *            The parameters to qualify the execution; must not be
+     *            <code>null</code>. This must be a map of parameter ids (<code>String</code>)
+     *            to parameter values (<code>String</code>).
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @since 3.2
+     */
+    public this(Command command, Map!(String,String) parameters,
+            Object trigger, Object applicationContext) {
+        if (parameters is null) {
+            throw new NullPointerException(
+                    "An execution event must have a non-null map of parameters"); //$NON-NLS-1$
+        }
+
+        this.command = command;
+        this.parameters = parameters;
+        this.trigger = trigger;
+        this.applicationContext = applicationContext;
+    }
+
+    /**
+     * Returns the state of the application at the time the execution was
+     * triggered.
+     *
+     * @return The application context; may be <code>null</code>.
+     */
+    public final Object getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * Returns the command being executed.
+     *
+     * @return The command being executed.
+     * @since 3.2
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns the object represented by the string value of the parameter with
+     * the provided id.
+     * <p>
+     * This is intended to be used in the scope of an
+     * {@link IHandler#execute(ExecutionEvent)} method, so any problem getting
+     * the object value causes <code>ExecutionException</code> to be thrown.
+     * </p>
+     *
+     * @param parameterId
+     *            The id of a parameter to retrieve the object value of.
+     * @return The object value of the parameter with the provided id.
+     * @throws ExecutionException
+     *             if the parameter object value could not be obtained for any
+     *             reason
+     * @since 3.2
+     */
+    public final Object getObjectParameterForExecution(String parameterId) {
+        if (command is null) {
+            throw new ExecutionException(
+                    "No command is associated with this execution event"); //$NON-NLS-1$
+        }
+
+        try {
+            ParameterType parameterType = command
+                    .getParameterType(parameterId);
+            if (parameterType is null) {
+                throw new ExecutionException(
+                        "Command does not have a parameter type for the given parameter"); //$NON-NLS-1$
+            }
+            AbstractParameterValueConverter valueConverter = parameterType
+                    .getValueConverter();
+            if (valueConverter is null) {
+                throw new ExecutionException(
+                        "Command does not have a value converter"); //$NON-NLS-1$
+            }
+            String stringValue = getParameter(parameterId);
+            Object objectValue = valueConverter
+                    .convertToObject(stringValue);
+            return objectValue;
+        } catch (NotDefinedException e) {
+            throw new ExecutionException("Command is not defined", e); //$NON-NLS-1$
+        } catch (ParameterValueConversionException e) {
+            throw new ExecutionException(
+                    "The parameter string could not be converted to an object", e); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the value of the parameter with the given id.
+     *
+     * @param parameterId
+     *            The id of the parameter to retrieve; may be <code>null</code>.
+     * @return The parameter value; <code>null</code> if the parameter cannot
+     *         be found.
+     */
+    public final String getParameter(String parameterId) {
+        return parameters.get(parameterId);
+    }
+
+    /**
+     * Returns all of the parameters.
+     *
+     * @return The parameters; never <code>null</code>, but may be empty.
+     */
+    public final Map!(String,String) getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Returns the object that triggered the execution
+     *
+     * @return The trigger; <code>null</code> if there was no trigger.
+     */
+    public final Object getTrigger() {
+        return trigger;
+    }
+
+    /**
+     * The string representation of this execution event -- for debugging
+     * purposes only. This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public final String toString() {
+        String parm;
+        foreach( k, v; parameters ){
+            parm ~= "{"~k~","~v~"}";
+        }
+        return Format( "ExecutionEvent({},{},{})", command, parm, trigger, applicationContext );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ExecutionException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.ExecutionException;
+
+import dwtx.core.commands.common.CommandException;
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an exception occured during the execution of a command.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ExecutionException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3258130262767448120L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @since 3.2
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/HandlerEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.HandlerEvent;
+
+import dwtx.core.commands.common.AbstractBitSetEvent;
+import dwtx.core.commands.IHandler;
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>IHandler</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IHandlerListener#handlerChanged(HandlerEvent)
+ */
+public final class HandlerEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the handler has changed its enabled
+     * state.
+     */
+    private static final int CHANGED_ENABLED = 1;
+
+    /**
+     * The bit used to represent whether the handler has changed its handled
+     * state.
+     */
+    private static final int CHANGED_HANDLED = 1 << 1;
+
+    /**
+     * The handler that changed; this value is never <code>null</code>.
+     */
+    private final IHandler handler;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param handler
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param enabledChanged
+     *            Whether the enabled state of the handler has changed.
+     * @param handledChanged
+     *            Whether the handled state of the handler has changed.
+     */
+    public this(IHandler handler, bool enabledChanged,
+            bool handledChanged) {
+        if (handler is null) {
+            throw new NullPointerException();
+        }
+        this.handler = handler;
+
+        if (enabledChanged) {
+            changedValues |= CHANGED_ENABLED;
+        }
+        if (handledChanged) {
+            changedValues |= CHANGED_HANDLED;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public IHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Returns whether or not the enabled property changed.
+     *
+     * @return <code>true</code>, iff the enabled property changed.
+     */
+    public bool isEnabledChanged() {
+        return ((changedValues & CHANGED_ENABLED) !is 0);
+    }
+
+    /**
+     * Returns whether or not the handled property changed.
+     *
+     * @return <code>true</code>, iff the handled property changed.
+     */
+    public bool isHandledChanged() {
+        return ((changedValues & CHANGED_HANDLED) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ICategoryListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.ICategoryListener;
+
+import dwtx.core.commands.CategoryEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>Category</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Category#addCategoryListener(ICategoryListener)
+ * @see Category#removeCategoryListener(ICategoryListener)
+ */
+public interface ICategoryListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>Category</code> have changed. Specific details are described in
+     * the <code>CategoryEvent</code>.
+     *
+     * @param categoryEvent
+     *            the category event. Guaranteed not to be <code>null</code>.
+     */
+    void categoryChanged(CategoryEvent categoryEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ICommandListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.ICommandListener;
+
+import dwtx.core.commands.CommandEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>Command</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Command#addCommandListener(ICommandListener)
+ * @see Command#removeCommandListener(ICommandListener)
+ */
+public interface ICommandListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>Command</code> have changed. Specific details are described in
+     * the <code>CommandEvent</code>.
+     *
+     * @param commandEvent
+     *            the command event. Guaranteed not to be <code>null</code>.
+     */
+    void commandChanged(CommandEvent commandEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ICommandManagerListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.ICommandManagerListener;
+
+import dwtx.core.commands.CommandManagerEvent;
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>ICommandManager</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see CommandManager#addCommandManagerListener(ICommandManagerListener)
+ * @see CommandManager#removeCommandManagerListener(ICommandManagerListener)
+ */
+public interface ICommandManagerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>ICommandManager</code> have changed. Specific details are
+     * described in the <code>CommandManagerEvent</code>.
+     *
+     * @param commandManagerEvent
+     *            the commandManager event. Guaranteed not to be
+     *            <code>null</code>.
+     */
+    void commandManagerChanged(CommandManagerEvent commandManagerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IExecutionListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.IExecutionListener;
+
+import dwtx.core.commands.NotHandledException;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.ExecutionEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A listener to the execution of commands. This listener will be notified if a
+ * command is about to execute, and when that execution completes. It is not
+ * possible for the listener to prevent the execution, only to respond to it in
+ * some way.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IExecutionListener {
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command with
+     * no handler.
+     *
+     * @param commandId
+     *            The identifier of command that is not handled; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notHandled(String commandId, NotHandledException exception);
+
+    /**
+     * Notifies the listener that a command has failed to complete execution.
+     *
+     * @param commandId
+     *            The identifier of the command that has executed; never
+     *            <code>null</code>.
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void postExecuteFailure(String commandId,
+            ExecutionException exception);
+
+    /**
+     * Notifies the listener that a command has completed execution
+     * successfully.
+     *
+     * @param commandId
+     *            The identifier of the command that has executed; never
+     *            <code>null</code>.
+     * @param returnValue
+     *            The return value from the command; may be <code>null</code>.
+     */
+    public void postExecuteSuccess(String commandId, Object returnValue);
+
+    /**
+     * Notifies the listener that a command is about to execute.
+     *
+     * @param commandId
+     *            The identifier of the command that is about to execute, never
+     *            <code>null</code>.
+     * @param event
+     *            The event that will be passed to the <code>execute</code>
+     *            method; never <code>null</code>.
+     */
+    public void preExecute(String commandId, ExecutionEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IExecutionListenerWithChecks.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.IExecutionListenerWithChecks;
+
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.commands.IExecutionListener;
+import dwtx.core.commands.NotEnabledException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A listener to the execution of commands. This listener will be notified if
+ * someone tries to execute a command and it is not enabled or not defined. The
+ * listener also be notified if a command is about to execute, and when that
+ * execution completes. It is not possible for the listener to prevent the
+ * execution, only to respond to it in some way.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IExecutionListenerWithChecks : IExecutionListener {
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command that
+     * is not defined.
+     *
+     * @param commandId
+     *            The identifier of command that is not defined; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notDefined(String commandId, NotDefinedException exception);
+
+    /**
+     * Notifies the listener that an attempt was made to execute a command that
+     * is disabled.
+     *
+     * @param commandId
+     *            The identifier of command that is not enabled; never
+     *            <code>null</code>
+     * @param exception
+     *            The exception that occurred; never <code>null</code>.
+     */
+    public void notEnabled(String commandId, NotEnabledException exception);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IHandler.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.IHandler;
+
+import dwtx.core.commands.IHandlerListener;
+import dwtx.core.commands.ExecutionEvent;
+
+
+/**
+ * A handler is the pluggable piece of a command that handles execution. Each
+ * command can have zero or more handlers associated with it (in general), of
+ * which only one will be active at any given moment in time. When the command
+ * is asked to execute, it will simply pass that request on to its active
+ * handler, if any.
+ *
+ * @see AbstractHandler
+ * @since 3.1
+ */
+public interface IHandler {
+
+    /**
+     * Registers an instance of <code>IHandlerListener</code> to listen for
+     * changes to properties of this instance.
+     *
+     * @param handlerListener
+     *            the instance to register. Must not be <code>null</code>. If
+     *            an attempt is made to register an instance which is already
+     *            registered with this instance, no operation is performed.
+     */
+    void addHandlerListener(IHandlerListener handlerListener);
+
+    /**
+     * Disposes of this handler. This method is run once when the object is no
+     * longer referenced. This can be used as an opportunity to unhook listeners
+     * from other objects.
+     */
+    public void dispose();
+
+    /**
+     * Executes with the map of parameter values by name.
+     *
+     * @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. Reserved for future use, must be
+     *         <code>null</code>.
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     */
+    Object execute(ExecutionEvent event);
+
+    /**
+     * Returns whether this handler is capable of executing at this moment in
+     * time.
+     *
+     * @return <code>true</code> if the command is enabled; <code>false</code>
+     *         otherwise.
+     */
+    public bool isEnabled();
+
+    /**
+     * Returns whether this handler is really capable of handling delegation. In
+     * the case of a handler that is a composition of other handlers, this reply
+     * is intended to indicate whether the handler is truly capable of receiving
+     * delegated responsibilities at this time.
+     *
+     * @return <code>true</code> if the handler is handled; <code>false</code>
+     *         otherwise.
+     */
+    public bool isHandled();
+
+    /**
+     * Unregisters an instance of <code>IHandlerListener</code> listening for
+     * changes to properties of this instance.
+     *
+     * @param handlerListener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    void removeHandlerListener(IHandlerListener handlerListener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IHandlerAttributes.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.IHandlerAttributes;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * Attribute constants that have special meanings within this package.  These
+ * attributes can be used to communicate extra information from the handler to
+ * either the command or the command manager.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IHandlerAttributes {
+
+    /**
+     * <p>
+     * The name of the attribute indicating whether the handler is handled.
+     * This is intended largely for backward compatibility with the workbench
+     * <code>RetargetAction</code> class.  It is used to indicate that while
+     * the handler is handling a command, it should not be treated as such.
+     * The command should act and behave as if it has no handler.
+     * </p>
+     */
+    public static final String ATTRIBUTE_HANDLED = "handled"; //$NON-NLS-1$
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IHandlerListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.IHandlerListener;
+
+import dwtx.core.commands.HandlerEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IHandler</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IHandler#addHandlerListener(IHandlerListener)
+ * @see IHandler#removeHandlerListener(IHandlerListener)
+ */
+public interface IHandlerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IHandler</code> have changed. Specific details are described in
+     * the <code>HandlerEvent</code>.
+     *
+     * @param handlerEvent
+     *            the handler event. Guaranteed not to be <code>null</code>.
+     */
+    void handlerChanged(HandlerEvent handlerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/INamedHandleStateIds.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.INamedHandleStateIds;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * State identifiers that are understood by named handle objects that implement
+ * {@link IObjectWithState}.
+ * </p>
+ * <p>
+ * Clients may implement or extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface INamedHandleStateIds {
+
+    /**
+     * The state id used for overriding the description of a named handle
+     * object. This state's value must return a {@link String}.
+     */
+    public static String DESCRIPTION = "DESCRIPTION"; //$NON-NLS-1$
+
+    /**
+     * The state id used for overriding the name of a named handle object. This
+     * state's value must return a {@link String}.
+     */
+    public static String NAME = "NAME"; //$NON-NLS-1$
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IObjectWithState.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.IObjectWithState;
+
+import dwtx.core.commands.State;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An object that holds zero or more state objects. This state information can
+ * be shared between different instances of <code>IObjectWithState</code>.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend this interface.
+ * </p>
+ *
+ * @see AbstractHandlerWithState
+ * @since 3.2
+ */
+public interface IObjectWithState {
+
+    /**
+     * Adds state to this object.
+     *
+     * @param id
+     *            The identifier indicating the type of state being added; must
+     *            not be <code>null</code>.
+     * @param state
+     *            The new state to add to this object; must not be
+     *            <code>null</code>.
+     */
+    public void addState(String id, State state);
+
+    /**
+     * Gets the state with the given id.
+     *
+     * @param stateId
+     *            The identifier of the state to retrieve; must not be
+     *            <code>null</code>.
+     * @return The state; may be <code>null</code> if there is no state with
+     *         the given id.
+     */
+    public State getState(String stateId);
+
+    /**
+     * Gets the identifiers for all of the state associated with this object.
+     *
+     * @return All of the state identifiers; may be empty, but never
+     *         <code>null</code>.
+     */
+    public String[] getStateIds();
+
+    /**
+     * Removes state from this object.
+     *
+     * @param stateId
+     *            The id of the state to remove from this object; must not be
+     *            <code>null</code>.
+     */
+    public void removeState(String stateId);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IParameter.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.IParameter;
+
+import dwtx.core.commands.IParameterValues;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A parameter for a command. A parameter identifies a type of information that
+ * the command might accept. For example, a "Show View" command might accept the
+ * id of a view for display. This parameter also identifies possible values, for
+ * display in the user interface.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IParameter {
+
+    /**
+     * Returns the identifier for this parameter.
+     *
+     * @return The identifier; never <code>null</code>.
+     */
+    public String getId();
+
+    /**
+     * Returns the human-readable name for this parameter.
+     *
+     * @return The parameter name; never <code>null</code>.
+     */
+    public String getName();
+
+    /**
+     * Returns the values associated with this parameter.
+     *
+     * @return The values associated with this parameter. This must not be
+     *         <code>null</code>.
+     * @throws ParameterValuesException
+     *             If the values can't be retrieved for some reason.
+     */
+    public IParameterValues getValues();
+
+    /**
+     * Returns whether parameter is optional. Otherwise, it is required.
+     *
+     * @return <code>true</code> if the parameter is optional;
+     *         <code>false</code> if it is required.
+     */
+    public bool isOptional();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IParameterTypeListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.IParameterTypeListener;
+
+import dwtx.core.commands.ParameterTypeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of {@link ParameterType}.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.2
+ * @see ParameterType#addListener(IParameterTypeListener)
+ * @see ParameterType#removeListener(IParameterTypeListener)
+ */
+public interface IParameterTypeListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * {@link ParameterType} have changed. Specific details are described in the
+     * {@link ParameterTypeEvent}.
+     *
+     * @param parameterTypeEvent
+     *            the event. Guaranteed not to be <code>null</code>.
+     */
+    void parameterTypeChanged(ParameterTypeEvent parameterTypeEvent);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IParameterValues.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.IParameterValues;
+
+import tango.util.collection.model.Map;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * The parameters for a command. This interface will only be consulted if the
+ * parameters need to be displayed to the user. Otherwise, they will be ignored.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IParameterValues {
+
+    /**
+     * Returns a map keyed by externalized names for parameter values. These
+     * names should be human-readable, and are generally for display to the user
+     * in a user interface of some sort. The values should be actual values that
+     * will be interpreted by the handler for the command.
+     *
+     * @return A map of the externalizable name of the parameter value (<code>String</code>)
+     *         to the actual value of the parameter (<code>String</code>).
+     */
+    public Map!(String,String) getParameterValues();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/IStateListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.IStateListener;
+
+import dwtx.core.commands.State;
+
+/**
+ * <p>
+ * A listener to changes in some state.
+ * </p>
+ * <p>
+ * Clients may implement, but must not extend this interface.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IStateListener {
+
+    /**
+     * Handles a change to the value in some state.
+     *
+     * @param state
+     *            The state that has changed; never <code>null</code>. The
+     *            value for this state has been updated to the new value.
+     * @param oldValue
+     *            The old value; may be anything.
+     */
+    public void handleStateChange(State state, Object oldValue);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ITypedParameter.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.ITypedParameter;
+
+import dwtx.core.commands.ParameterType;
+
+/**
+ * A command parameter that has a declared type. This interface is intended to
+ * be implemented by implementors of {@link IParameter} that will support
+ * parameter types.
+ *
+ * @since 3.2
+ */
+public interface ITypedParameter {
+
+    /**
+     * Returns the {@link ParameterType} associated with a command parameter or
+     * <code>null</code> if the parameter does not declare a type.
+     * <p>
+     * Note that the parameter type returned may be undefined.
+     * </p>
+     *
+     * @return the parameter type associated with a command parameter or
+     *         <code>null</code> if the parameter does not declare a type
+     */
+    public ParameterType getParameterType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/NamedHandleObjectWithState.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.NamedHandleObjectWithState;
+
+import tango.util.collection.HashMap;
+import tango.util.collection.model.Map;
+// import tango.util.collection.model.Set;
+
+import dwtx.core.commands.common.NamedHandleObject;
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.commands.IObjectWithState;
+import dwtx.core.commands.State;
+import dwtx.core.commands.INamedHandleStateIds;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A named handle object that can carry state with it. This state can be used to
+ * override the name or description.
+ * </p>
+ * <p>
+ * Clients may neither instantiate nor extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+abstract class NamedHandleObjectWithState : NamedHandleObject,
+        IObjectWithState {
+
+    /**
+     * An empty string array, which can be returned from {@link #getStateIds()}
+     * if there is no state.
+     */
+    private static const String[] NO_STATE = null;
+
+    /**
+     * The map of states currently held by this command. If this command has no
+     * state, then this will be <code>null</code>.
+     */
+    private Map!(String,State) states = null;
+
+    /**
+     * Constructs a new instance of <code>NamedHandleObject<WithState/code>.
+     *
+     * @param id
+     *            The identifier for this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        super(id);
+    }
+
+    public void addState(String stateId, State state) {
+        if (state is null) {
+            throw new NullPointerException("Cannot add a null state"); //$NON-NLS-1$
+        }
+
+        if (states is null) {
+            states = new HashMap!(String,State)/+(3)+/;
+        }
+        states.add(stateId, state);
+    }
+
+    public final String getDescription() {
+        String description = super.getDescription(); // Trigger a NDE.
+
+        State descriptionState = getState(INamedHandleStateIds.DESCRIPTION);
+        if (descriptionState !is null) {
+            Object value = descriptionState.getValue();
+            if (value !is null) {
+                return value.toString();
+            }
+        }
+
+        return description;
+    }
+
+    public final String getName() {
+        String name = super.getName(); // Trigger a NDE, if necessary.
+
+        State nameState = getState(INamedHandleStateIds.NAME);
+        if (nameState !is null) {
+            final Object value = nameState.getValue();
+            if (value !is null) {
+                return value.toString();
+            }
+        }
+
+        return name;
+    }
+
+    public final State getState(String stateId) {
+        if ((states is null) || (states.drained())) {
+            return null;
+        }
+
+        return states.get(stateId);
+    }
+
+    public final String[] getStateIds() {
+        if ((states is null) || (states.drained())) {
+            return NO_STATE;
+        }
+
+        String[] res;
+        foreach( id, state; states){
+            res ~= id;
+        }
+        return res;
+    }
+
+    public void removeState(String id) {
+        if (id is null) {
+            throw new NullPointerException("Cannot remove a null id"); //$NON-NLS-1$
+        }
+
+        if (states !is null) {
+            states.removeKey(id);
+            if (states.drained()) {
+                states = null;
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/NotEnabledException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.NotEnabledException;
+
+import dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an attempt was made to execute a command that is not enabled.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class NotEnabledException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3257572788998124596L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/NotHandledException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.NotHandledException;
+
+import dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an attempt was made to access the properties of an unhandled
+ * object.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class NotHandledException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3256446914827726904L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ParameterType.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.ParameterType;
+
+import dwtx.core.commands.common.HandleObject;
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.internal.commands.util.Util;
+import dwtx.core.commands.AbstractParameterValueConverter;
+import dwtx.core.commands.IParameterTypeListener;
+import dwtx.core.commands.ParameterTypeEvent;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * <p>
+ * Provides information about the type of a command parameter. Clients can use a
+ * parameter type to check if an object matches the type of the parameter with
+ * {@link #isCompatible(Object)} and can get an
+ * {@link AbstractParameterValueConverter} to convert between objects matching
+ * the parameter type and strings that encode the object's identity.
+ * </p>
+ * <p>
+ * A command parameter is not required to declare a type. To determine if a
+ * given parameter has a type, check if an {@link IParameter} implements
+ * {@link ITypedParameter} and if so, use
+ * {@link ITypedParameter#getParameterType()} like this:
+ * </p>
+ *
+ * <pre>
+ *                   IParameter parameter = // ... get IParameter from Command
+ *                   if (parameter instanceof ITypedParameter) {
+ *                     ParameterType type = ((ITypedParameter)parameter).getParameterType();
+ *                     if (type !is null) {
+ *                       // this parameter has a ParameterType
+ *                     }
+ *                   }
+ * </pre>
+ *
+ * @see IParameter
+ * @see ITypedParameter#getParameterType()
+ * @since 3.2
+ */
+public final class ParameterType : HandleObject, Comparable {
+
+    /**
+     * TODO: this was copied from
+     * dwtx.core.internal.expressions.Expressions is there a better place
+     * to reference this?
+     *
+     * @param element
+     *            The element to test; may be <code>null</code>.
+     * @param type
+     *            The type against which we are testing;may be <code>null</code>.
+     * @return <code>true</code> if the <code>element</code> is an instance
+     *         of <code>type</code>; <code>false</code> otherwise.
+     */
+    private static final bool isInstanceOf(Object element,
+            String type) {
+        // null isn't an instanceof of anything.
+        if (element is null) {
+            return false;
+        }
+        return isSubtype(element.classinfo, type);
+    }
+
+    /**
+     * TODO: this was copied from
+     * dwtx.core.internal.expressions.Expressions is there a better place
+     * to reference this?
+     *
+     * @param clazz
+     *            The class to match; may be <code>null</code>.
+     * @param type
+     *            The type against which we are testing;may be <code>null</code>.
+     * @return <code>true</code> if the <code>element</code> is an instance
+     *         of <code>type</code>; <code>false</code> otherwise.
+     */
+    private static final bool isSubtype(ClassInfo clazz, String type) {
+        if (clazz.name.equals(type)) {
+            return true;
+        }
+        ClassInfo superClass = clazz.base;
+        if (superClass !is null && isSubtype(superClass, type)) {
+            return true;
+        }
+        Interface[] interfaces = clazz.interfaces;
+        for (int i = 0; i < interfaces.length; i++) {
+            if (isSubtype(interfaces[i].classinfo, type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * An {@link AbstractParameterValueConverter} for converting parameter
+     * values between objects and strings. This may be <code>null</code>.
+     */
+    private /+transient+/ AbstractParameterValueConverter parameterTypeConverter;
+
+    /**
+     * A string specifying the object type of this parameter type. This will be
+     * <code>null</code> when the parameter type is undefined but never null
+     * when it is defined.
+     */
+    private /+transient+/ String type = null;
+
+    /**
+     * Constructs a new instance based on the given identifier. When a parameter
+     * type is first constructed, it is undefined. Parameter types should only
+     * be constructed by the {@link CommandManager} to ensure that the
+     * identifier remains unique.
+     *
+     * @param id
+     *            The identifier for this type. This value must not be
+     *            <code>null</code>, and must be unique amongst all parameter
+     *            types.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Adds a listener to this parameter type that will be notified when its
+     * state changes.
+     *
+     * @param listener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    public final void addListener(IParameterTypeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Compares this parameter type with another object by comparing each of the
+     * non-transient attributes.
+     *
+     * @param object
+     *            The object with which to compare; must be an instance of
+     *            {@link ParameterType}.
+     * @return A negative integer, zero or a positive integer, if the object is
+     *         greater than, equal to or less than this parameter type.
+     */
+    public final int compareTo(Object object) {
+        ParameterType castedObject = cast(ParameterType) object;
+        int compareTo = Util.compare(defined, castedObject.defined);
+        if (compareTo is 0) {
+            compareTo = Util.compare(id, castedObject.id);
+        }
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this parameter type, setting the defined property to
+     * <code>true</code>.
+     * </p>
+     * <p>
+     * Notification is sent to all listeners that something has changed.
+     * </p>
+     *
+     * @param type
+     *            a string identifying the Java object type for this parameter
+     *            type; <code>null</code> is interpreted as
+     *            <code>"java.lang.Object"</code>
+     * @param parameterTypeConverter
+     *            an {@link AbstractParameterValueConverter} to perform
+     *            string/object conversions for parameter values; may be
+     *            <code>null</code>
+     */
+    public final void define(String type,
+            AbstractParameterValueConverter parameterTypeConverter) {
+
+        bool definedChanged = !this.defined;
+        this.defined = true;
+
+        this.type = (type is null) ? Object.classinfo.name : type;
+        this.parameterTypeConverter = parameterTypeConverter;
+
+        fireParameterTypeChanged(new ParameterTypeEvent(this, definedChanged));
+    }
+
+    /**
+     * Notifies all listeners that this parameter type has changed. This sends
+     * the given event to all of the listeners, if any.
+     *
+     * @param event
+     *            The event to send to the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireParameterTypeChanged(ParameterTypeEvent event) {
+        if (event is null) {
+            throw new NullPointerException(
+                    "Cannot send a null event to listeners."); //$NON-NLS-1$
+        }
+
+        if (!isListenerAttached()) {
+            return;
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IParameterTypeListener listener = cast(IParameterTypeListener) listeners[i];
+            listener.parameterTypeChanged(event);
+        }
+    }
+
+    /**
+     * Returns the value converter associated with this parameter, if any.
+     *
+     * @return The parameter value converter, or <code>null</code> if there is
+     *         no value converter for this parameter.
+     * @throws NotDefinedException
+     *             if the parameter type is not currently defined
+     */
+    public final AbstractParameterValueConverter getValueConverter() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot use getValueConverter() with an undefined ParameterType"); //$NON-NLS-1$
+        }
+
+        return parameterTypeConverter;
+    }
+
+    /**
+     * Returns whether the provided value is compatible with this parameter
+     * type. An object is compatible with a parameter type if the object is an
+     * instance of the class defined as the parameter's type class.
+     *
+     * @param value
+     *            an object to check for compatibility with this parameter type;
+     *            may be <code>null</code>.
+     * @return <code>true</code> if the value is compatible with this type,
+     *         <code>false</code> otherwise
+     * @throws NotDefinedException
+     *             if the parameter type is not currently defined
+     */
+    public bool isCompatible(Object value) {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot use isCompatible() with an undefined ParameterType"); //$NON-NLS-1$
+        }
+        return isInstanceOf(value, type);
+    }
+
+    /**
+     * Unregisters listener for changes to properties of this parameter type.
+     *
+     * @param listener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void removeListener(IParameterTypeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * The string representation of this parameter type. 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 = Format( "ParameterType({},{})", id, defined );
+        }
+        return string;
+    }
+
+    /**
+     * Makes this parameter type become undefined. Notification is sent to all
+     * listeners.
+     */
+    public final void undefine() {
+        string = null;
+
+        final bool definedChanged = defined;
+        defined = false;
+
+        type = null;
+        parameterTypeConverter = null;
+
+        fireParameterTypeChanged(new ParameterTypeEvent(this, definedChanged));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ParameterTypeEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.ParameterTypeEvent;
+
+import dwtx.core.commands.common.AbstractHandleObjectEvent;
+import dwtx.core.commands.ParameterType;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * {@link ParameterType}.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @see IParameterTypeListener#parameterTypeChanged(ParameterTypeEvent)
+ * @since 3.2
+ */
+public final class ParameterTypeEvent : AbstractHandleObjectEvent {
+
+    /**
+     * The parameter type that has changed. This value is never
+     * <code>null</code>.
+     */
+    private final ParameterType parameterType;
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param parameterType
+     *            The parameter type that changed; must not be <code>null</code>.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     */
+    this(ParameterType parameterType,
+            bool definedChanged) {
+
+        super(definedChanged);
+
+        if (parameterType is null) {
+            throw new NullPointerException();
+        }
+
+        this.parameterType = parameterType;
+    }
+
+    /**
+     * Returns the instance of the parameter type that changed.
+     *
+     * @return the instance of the parameter type that changed. Guaranteed not
+     *         to be <code>null</code>.
+     */
+    public final ParameterType getParameterType() {
+        return parameterType;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ParameterValueConversionException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.ParameterValueConversionException;
+
+import dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that a problem occurred while converting a command parameter value
+ * from string to object, or object to string.
+ *
+ * @see AbstractParameterValueConverter
+ * @since 3.2
+ */
+public class ParameterValueConversionException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 4703077729505066104L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message,
+            Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ParameterValuesException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.ParameterValuesException;
+
+import dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * Signals that a problem has occurred while trying to create an instance of
+ * <code>IParameterValues</code>. In applications based on the registry
+ * provided by core, this usually indicates a problem creating an
+ * <code>IExecutableExtension</code>. For other applications, this exception
+ * could be used to signify any general problem during initialization.
+ * </p>
+ *
+ * @since 3.1
+ *
+ */
+public final class ParameterValuesException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 3618976793520845623L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/Parameterization.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.Parameterization;
+
+import tango.util.collection.model.Map;
+
+import dwtx.core.commands.IParameter;
+import dwtx.core.internal.commands.util.Util;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A parameter with a specific value. This is usually a part of a
+ * <code>ParameterizedCommand</code>, which is used to refer to a command
+ * with a collection of parameterizations.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class Parameterization {
+
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all parameterized commands.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all parameterized commands.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL = dwt.dwthelper.utils.toHash( Parameterization.classinfo.name );
+    }
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ hash_t hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The parameter that is being parameterized. This value is never
+     * <code>null</code>.
+     */
+    private const IParameter parameter;
+
+    /**
+     * The value that defines the parameterization. This value may be
+     * <code>null</code>.
+     */
+    private const String value;
+
+    /**
+     * Constructs a new instance of <code>Parameterization</code>.
+     *
+     * @param parameter
+     *            The parameter that is being parameterized; must not be
+     *            <code>null</code>.
+     * @param value
+     *            The value for the parameter; may be <code>null</code>.
+     */
+    public this(IParameter parameter, String value) {
+        if (parameter is null) {
+            throw new NullPointerException(
+                    "You cannot parameterize a null parameter"); //$NON-NLS-1$
+        }
+
+        this.parameter = parameter;
+        this.value = value;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public final bool equals(Object object) {
+        if (this is object) {
+            return true;
+        }
+
+        if (!(cast(Parameterization)object)) {
+            return false;
+        }
+
+        Parameterization parameterization = cast(Parameterization) object;
+        if (!(Util.equals(this.parameter.getId(), parameterization.parameter
+                .getId()))) {
+            return false;
+        }
+
+        return Util.equals(this.value, parameterization.value);
+    }
+
+    /**
+     * Returns the parameter that is being parameterized.
+     *
+     * @return The parameter; never <code>null</code>.
+     */
+    public final IParameter getParameter() {
+        return parameter;
+    }
+
+    /**
+     * Returns the value for the parameter in this parameterization.
+     *
+     * @return The value; may be <code>null</code>.
+     */
+    public final String getValue() {
+        return value;
+    }
+
+    /**
+     * Returns the human-readable name for the current value, if any. If the
+     * name cannot be found, then it simply returns the value. It also ensures
+     * that any <code>null</code> values are converted into an empty string.
+     *
+     * @return The human-readable name of the value; never <code>null</code>.
+     * @throws ParameterValuesException
+     *             If the parameter needed to be initialized, but couldn't be.
+     */
+    public final String getValueName() {
+        auto parameterValues = parameter.getValues().getParameterValues();
+        String returnValue = null;
+        foreach( k, v; parameterValues ){
+            if (Util.equals(value, v)) {
+                returnValue = k;
+                break;
+            }
+        }
+        if (returnValue is null) {
+            return Util.ZERO_LENGTH_STRING;
+        }
+
+        return returnValue;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override final hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(cast(Object)parameter);
+            hashCode = hashCode * HASH_FACTOR + Util.toHash(value);
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/ParameterizedCommand.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,666 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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 dwtx.core.commands.ParameterizedCommand;
+
+// import java.util.ArrayList;
+// import java.util.Collection;
+// import java.util.Collections;
+// import java.util.HashMap;
+// import java.util.Iterator;
+// import java.util.List;
+// import java.util.Map;
+
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.Map;
+import tango.util.collection.model.Set;
+import tango.util.collection.ArraySeq;
+import tango.util.collection.HashSet;
+import tango.util.collection.HashMap;
+
+import dwtx.core.commands.Command;
+import dwtx.core.commands.CommandManager;
+import dwtx.core.commands.IParameter;
+import dwtx.core.commands.IParameterValues;
+import dwtx.core.commands.Parameterization;
+import dwtx.core.commands.ParameterValuesException;
+import dwtx.core.commands.ExecutionEvent;
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.internal.commands.util.Util;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * A command that has had one or more of its parameters specified. This class
+ * serves as a utility class for developers that need to manipulate commands
+ * with parameters. It handles the behaviour of generating a parameter map and a
+ * human-readable name.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ParameterizedCommand : Comparable {
+    private static const Map!(String,String) EMPTY_MAP;
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all parameterized commands.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all parameterized commands.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL = dwt.dwthelper.utils.toHash(ParameterizedCommand.classinfo.name );
+        EMPTY_MAP = new HashMap!(String,String);
+    }
+    /**
+     * The index of the parameter id in the parameter values.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_ID = 0;
+
+    /**
+     * The index of the human-readable name of the parameter itself, in the
+     * parameter values.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_NAME = 1;
+
+    /**
+     * The index of the human-readable name of the value of the parameter for
+     * this command.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_VALUE_NAME = 2;
+
+    /**
+     * The index of the value of the parameter that the command can understand.
+     *
+     * @deprecated no longer used
+     */
+    public static const int INDEX_PARAMETER_VALUE_VALUE = 3;
+
+    /**
+     * Escapes special characters in the command id, parameter ids and parameter
+     * values for {@link #serialize()}. The special characters
+     * {@link CommandManager#PARAMETER_START_CHAR},
+     * {@link CommandManager#PARAMETER_END_CHAR},
+     * {@link CommandManager#ID_VALUE_CHAR},
+     * {@link CommandManager#PARAMETER_SEPARATOR_CHAR} and
+     * {@link CommandManager#ESCAPE_CHAR} are escaped by prepending a
+     * {@link CommandManager#ESCAPE_CHAR} character.
+     *
+     * @param rawText
+     *            a <code>String</code> to escape special characters in for
+     *            serialization.
+     * @return a <code>String</code> representing <code>rawText</code> with
+     *         special serialization characters escaped
+     * @see CommandManager#unescape(String)
+     * @since 3.2
+     */
+    private static final String escape(String rawText) {
+
+        // defer initialization of a StringBuffer until we know we need one
+        StringBuffer buffer;
+
+        for (int i = 0; i < rawText.length; i++) {
+
+            char c = rawText.charAt(i);
+            switch (c) {
+            case CommandManager.PARAMETER_START_CHAR:
+            case CommandManager.PARAMETER_END_CHAR:
+            case CommandManager.ID_VALUE_CHAR:
+            case CommandManager.PARAMETER_SEPARATOR_CHAR:
+            case CommandManager.ESCAPE_CHAR:
+                if (buffer is null) {
+                    buffer = new StringBuffer(rawText.substring(0, i));
+                }
+                buffer.append(CommandManager.ESCAPE_CHAR);
+                buffer.append(c);
+                break;
+            default:
+                if (buffer !is null) {
+                    buffer.append(c);
+                }
+                break;
+            }
+
+        }
+
+        if (buffer is null) {
+            return rawText;
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Generates every possible combination of parameter values for the given
+     * parameters. Parameters values that cannot be initialized are just
+     * ignored. Optional parameters are considered.
+     *
+     * @param startIndex
+     *            The index in the <code>parameters</code> that we should
+     *            process. This must be a valid index.
+     * @param parameters
+     *            The parameters in to process; must not be <code>null</code>.
+     * @return A collection (<code>Collection</code>) of combinations (<code>List</code>
+     *         of <code>Parameterization</code>).
+     */
+    private static final Seq!(Object) expandParameters(int startIndex,
+            IParameter[] parameters) {
+        int nextIndex = startIndex + 1;
+        bool noMoreParameters = (nextIndex >= parameters.length);
+
+        IParameter parameter = parameters[startIndex];
+        auto parameterizations = new ArraySeq!(Object);
+        if (parameter.isOptional()) {
+            parameterizations.append( cast(Object) null);
+        }
+
+        IParameterValues values = null;
+        try {
+            values = parameter.getValues();
+        } catch (ParameterValuesException e) {
+            if (noMoreParameters) {
+                return parameterizations;
+            }
+
+            // Make recursive call
+            return expandParameters(nextIndex, parameters);
+        }
+
+//         Iterator parameterValueItr = parameterValues.entrySet()
+//                 .iterator();
+        auto parameterValues = values.getParameterValues();
+        auto set = new HashSet!(String);
+        foreach( k,v; parameterValues ){
+            set.add( v );
+        }
+
+
+        //while (parameterValueItr.hasNext()) {
+        foreach( v; set ){
+            //Map.Entry entry = (Map.Entry) parameterValueItr.next();
+            Parameterization parameterization = new Parameterization(
+                    parameter, v);
+            parameterizations.append(parameterization);
+        }
+
+        // Check if another iteration will produce any more names.
+        int parameterizationCount = parameterizations.size();
+        if (noMoreParameters) {
+            // This is it, so just return the current parameterizations.
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                auto combination = new ArraySeq!(Object);
+                combination.append(parameterization);
+                parameterizations.replaceAt(i, combination);
+            }
+            return parameterizations;
+        }
+
+        // Make recursive call
+        auto suffixes = expandParameters(nextIndex, parameters);
+        suffixes.removeAll(cast(Object)null);
+        if (suffixes.drained()) {
+            // This is it, so just return the current parameterizations.
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                auto combination = new ArraySeq!(Object);
+                combination.append(parameterization);
+                parameterizations.replaceAt(i, combination);
+            }
+            return parameterizations;
+        }
+        auto returnValue = new ArraySeq!(Object);
+//         Iterator suffixItr = suffixes.iterator();
+//         while (suffixItr.hasNext()) {
+        foreach( v; suffixes ){
+//             final List combination = (List) suffixItr.next();
+            auto combination = cast(Seq!(Object)) v;
+            int combinationSize = combination.size();
+            for (int i = 0; i < parameterizationCount; i++) {
+                Parameterization parameterization = cast(Parameterization) parameterizations
+                        .get(i);
+                auto newCombination = new ArraySeq!(Object);
+                newCombination.capacity(combinationSize + 1);
+                newCombination.append(parameterization);
+                foreach( c; combination ){
+                    newCombination.append(c);
+                }
+                returnValue.append(newCombination);
+            }
+        }
+
+        return returnValue;
+    }
+
+    /**
+     * <p>
+     * Generates all the possible combinations of command parameterizations for
+     * the given command. If the command has no parameters, then this is simply
+     * a parameterized version of that command. If a parameter is optional, both
+     * the included and not included cases are considered.
+     * </p>
+     * <p>
+     * If one of the parameters cannot be loaded due to a
+     * <code>ParameterValuesException</code>, then it is simply ignored.
+     * </p>
+     *
+     * @param command
+     *            The command for which the parameter combinations should be
+     *            generated; must not be <code>null</code>.
+     * @return A collection of <code>ParameterizedCommand</code> instances
+     *         representing all of the possible combinations. This value is
+     *         never empty and it is never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the command is not defined.
+     */
+    public static final Seq!(Object) generateCombinations(Command command) {
+        IParameter[] parameters = command.getParameters();
+        if (parameters is null) {
+            auto res = new ArraySeq!(Object);
+            res.append( new ParameterizedCommand(command, null) );
+            return res;
+        }
+
+        auto expansion = expandParameters(0, parameters);
+        auto combinations = new ArraySeq!(Object);
+        combinations.capacity(expansion.size());
+        foreach( v; expansion ){
+//         Iterator expansionItr = expansion.iterator();
+//         while (expansionItr.hasNext()) {
+            auto combination = cast(Seq!(Object)) v;
+            if (combination is null) {
+                combinations.append(new ParameterizedCommand(command, null));
+            } else {
+                combination.removeAll(cast(Object)null);
+                if (combination.drained()) {
+                    combinations.append(new ParameterizedCommand(command, null));
+                } else {
+                    Parameterization[] parameterizations = cast(Parameterization[]) combination
+                            .toArray();
+                    combinations.append(new ParameterizedCommand(command,
+                            parameterizations));
+                }
+            }
+        }
+
+        return combinations;
+    }
+
+    /**
+     * The base command which is being parameterized. This value is never
+     * <code>null</code>.
+     */
+    private const Command command;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * This is an array of parameterization defined for this command. This value
+     * may be <code>null</code> if the command has no parameters.
+     */
+    private const Parameterization[] parameterizations;
+
+    private String name;
+
+    /**
+     * Constructs a new instance of <code>ParameterizedCommand</code> with
+     * specific values for zero or more of its parameters.
+     *
+     * @param command
+     *            The command that is parameterized; must not be
+     *            <code>null</code>.
+     * @param parameterizations
+     *            An array of parameterizations binding parameters to values for
+     *            the command. This value may be <code>null</code>. This
+     *            argument is not copied; if you need to make changes to it
+     *            after constructing this parameterized command, then make a
+     *            copy yourself.
+     */
+    public this(Command command,
+            Parameterization[] parameterizations) {
+        if (command is null) {
+            throw new NullPointerException(
+                    "A parameterized command cannot have a null command"); //$NON-NLS-1$
+        }
+
+        this.command = command;
+        this.parameterizations = (parameterizations is null || parameterizations.length is 0) ? null
+                : parameterizations;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        ParameterizedCommand command = cast(ParameterizedCommand) object;
+        bool thisDefined = this.command.isDefined();
+        bool otherDefined = command.command.isDefined();
+        if (!thisDefined || !otherDefined) {
+            return Util.compare(thisDefined, otherDefined);
+        }
+
+        try {
+            int compareTo = getName() < command.getName();
+            if (compareTo is 0) {
+                return getId() < command.getId();
+            }
+            return compareTo;
+        } catch (NotDefinedException e) {
+            throw new Exception (
+                    "Concurrent modification of a command's defined state"); //$NON-NLS-1$
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public final bool equals(Object object) {
+        if (this is object) {
+            return true;
+        }
+
+        if (!(cast(ParameterizedCommand)object)) {
+            return false;
+        }
+
+        ParameterizedCommand command = cast(ParameterizedCommand) object;
+        if (!Util.equals(this.command, command.command)) {
+            return false;
+        }
+
+        return Util.equals(this.parameterizations, command.parameterizations);
+    }
+
+    /**
+     * Executes this command with its parameters. This method will succeed
+     * regardless of whether the command is enabled or defined. It is
+     * preferrable to use {@link #executeWithChecks(Object, Object)}.
+     *
+     * @param trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>.
+     * @throws ExecutionException
+     *             If the handler has problems executing this command.
+     * @throws NotHandledException
+     *             If there is no handler.
+     * @deprecated Please use {@link #executeWithChecks(Object, Object)}
+     *             instead.
+     */
+    public final Object execute(Object trigger,
+            Object applicationContext) {
+        return command.execute(new ExecutionEvent(command, getParameterMap(),
+                trigger, applicationContext));
+    }
+
+    /**
+     * Executes this command with its parameters. This does extra checking 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 trigger
+     *            The object that triggered the execution; may be
+     *            <code>null</code>.
+     * @param applicationContext
+     *            The state of the application at the time the execution was
+     *            triggered; may be <code>null</code>.
+     * @return The result of the execution; may be <code>null</code>.
+     * @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(Object trigger,
+            Object applicationContext) {
+        return command.executeWithChecks(new ExecutionEvent(command,
+                getParameterMap(), trigger, applicationContext));
+    }
+
+    /**
+     * Returns the base command. It is possible for more than one parameterized
+     * command to have the same identifier.
+     *
+     * @return The command; never <code>null</code>, but may be undefined.
+     */
+    public final Command getCommand() {
+        return command;
+    }
+
+    /**
+     * Returns the command's base identifier. It is possible for more than one
+     * parameterized command to have the same identifier.
+     *
+     * @return The command id; never <code>null</code>.
+     */
+    public final String getId() {
+        return command.getId();
+    }
+
+    /**
+     * Returns a human-readable representation of this command with all of its
+     * parameterizations.
+     *
+     * @return The human-readable representation of this parameterized command;
+     *         never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the underlying command is not defined.
+     */
+    public final String getName() {
+        if (name is null) {
+            StringBuffer nameBuffer = new StringBuffer();
+            nameBuffer.append(command.getName());
+            if (parameterizations !is null) {
+                nameBuffer.append(" ("); //$NON-NLS-1$
+                int parameterizationCount = parameterizations.length;
+                for (int i = 0; i < parameterizationCount; i++) {
+                    Parameterization parameterization = parameterizations[i];
+                    nameBuffer
+                            .append(parameterization.getParameter().getName());
+                    nameBuffer.append(": "); //$NON-NLS-1$
+                    try {
+                        nameBuffer.append(parameterization.getValueName());
+                    } catch (ParameterValuesException e) {
+                        /*
+                         * Just let it go for now. If someone complains we can
+                         * add more info later.
+                         */
+                    }
+
+                    // If there is another item, append a separator.
+                    if (i + 1 < parameterizationCount) {
+                        nameBuffer.append(", "); //$NON-NLS-1$
+                    }
+                }
+                nameBuffer.append(')');
+            }
+            name = nameBuffer.toString();
+        }
+        return name;
+    }
+
+    /**
+     * Returns the parameter map, as can be used to construct an
+     * <code>ExecutionEvent</code>.
+     *
+     * @return The map of parameter ids (<code>String</code>) to parameter
+     *         values (<code>String</code>). This map is never
+     *         <code>null</code>, but may be empty.
+     */
+    public final Map!(String,String) getParameterMap() {
+        if ((parameterizations is null) || (parameterizations.length is 0)) {
+            return EMPTY_MAP;
+        }
+
+        auto parameterMap = new HashMap!(String,String);
+        for (int i = 0; i < parameterizations.length; i++) {
+            Parameterization parameterization = parameterizations[i];
+            parameterMap.add(parameterization.getParameter().getId(),
+                    parameterization.getValue());
+        }
+        return parameterMap;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public override final hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(command);
+            hashCode = hashCode * HASH_FACTOR;
+            if (parameterizations !is null) {
+                for (int i = 0; i < parameterizations.length; i++) {
+                    hashCode += Util.toHash(parameterizations[i]);
+                }
+            }
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+
+    /**
+     * Returns a {@link String} containing the command id, parameter ids and
+     * parameter values for this {@link ParameterizedCommand}. The returned
+     * {@link String} can be stored by a client and later used to reconstruct an
+     * equivalent {@link ParameterizedCommand} using the
+     * {@link CommandManager#deserialize(String)} method.
+     * <p>
+     * The syntax of the returned {@link String} is as follows:
+     * </p>
+     *
+     * <blockquote>
+     * <code>serialization = <u>commandId</u> [ '(' parameters ')' ]</code><br>
+     * <code>parameters = parameter [ ',' parameters ]</code><br>
+     * <code>parameter = <u>parameterId</u> [ '=' <u>parameterValue</u> ]</code>
+     * </blockquote>
+     *
+     * <p>
+     * In the syntax above, sections inside square-brackets are optional. The
+     * characters in single quotes (<code>(</code>, <code>)</code>,
+     * <code>,</code> and <code>=</code>) indicate literal characters.
+     * </p>
+     * <p>
+     * <code><u>commandId</u></code> represents the command id encoded with
+     * separator characters escaped. <code><u>parameterId</u></code> and
+     * <code><u>parameterValue</u></code> represent the parameter ids and
+     * values encoded with separator characters escaped. The separator
+     * characters <code>(</code>, <code>)</code>, <code>,</code> and
+     * <code>=</code> are escaped by prepending a <code>%</code>. This
+     * requires <code>%</code> to be escaped, which is also done by prepending
+     * a <code>%</code>.
+     * </p>
+     * <p>
+     * The order of the parameters is not defined (and not important). A missing
+     * <code><u>parameterValue</u></code> indicates that the value of the
+     * parameter is <code>null</code>.
+     * </p>
+     * <p>
+     * For example, the string shown below represents a serialized parameterized
+     * command that can be used to show the Resource perspective:
+     * </p>
+     * <p>
+     * <code>dwtx.ui.perspectives.showPerspective(dwtx.ui.perspectives.showPerspective.perspectiveId=dwtx.ui.resourcePerspective)</code>
+     * </p>
+     * <p>
+     * This example shows the more general form with multiple parameters,
+     * <code>null</code> value parameters, and escaped <code>=</code> in the
+     * third parameter value.
+     * </p>
+     * <p>
+     * <code>command.id(param1.id=value1,param2.id,param3.id=esc%=val3)</code>
+     * </p>
+     *
+     * @return A string containing the escaped command id, parameter ids and
+     *         parameter values; never <code>null</code>.
+     * @see CommandManager#deserialize(String)
+     * @since 3.2
+     */
+    public final String serialize() {
+        String escapedId = escape(getId());
+
+        if ((parameterizations is null) || (parameterizations.length is 0)) {
+            return escapedId;
+        }
+
+        StringBuffer buffer = new StringBuffer(escapedId);
+        buffer.append(CommandManager.PARAMETER_START_CHAR);
+
+        for (int i = 0; i < parameterizations.length; i++) {
+
+            if (i > 0) {
+                // insert separator between parameters
+                buffer.append(CommandManager.PARAMETER_SEPARATOR_CHAR);
+            }
+
+            Parameterization parameterization = parameterizations[i];
+            String parameterId = parameterization.getParameter().getId();
+            String escapedParameterId = escape(parameterId);
+
+            buffer.append(escapedParameterId);
+
+            String parameterValue = parameterization.getValue();
+            if (parameterValue !is null) {
+                String escapedParameterValue = escape(parameterValue);
+                buffer.append(CommandManager.ID_VALUE_CHAR);
+                buffer.append(escapedParameterValue);
+            }
+        }
+
+        buffer.append(CommandManager.PARAMETER_END_CHAR);
+
+        return buffer.toString();
+    }
+
+    public override final String toString() {
+        return Format( "ParameterizedCommand({},{})", command, parameterizations);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/SerializationException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.SerializationException;
+
+import dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an exception occured while serializing a
+ * {@link ParameterizedCommand} to a string or deserializing a String to a
+ * {@link ParameterizedCommand}.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class SerializationException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     */
+    private static final long serialVersionUID = 2691599674561684949L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/State.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.State;
+
+import dwtx.core.commands.common.EventManager;
+import dwtx.core.internal.commands.util.Util;
+import dwtx.core.commands.IStateListener;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A piece of state information that can be shared between objects, and might be
+ * persisted between sessions. This can be used for commands that toggle between
+ * two states and wish to pass this state information between different
+ * handlers.
+ * </p>
+ * <p>
+ * This state object can either be used as a single state object shared between
+ * several commands, or one state object per command -- depending on the needs
+ * of the application.
+ * </p>
+ * <p>
+ * Clients may instantiate or extend this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class State : EventManager {
+
+    /**
+     * The identifier of the state; may be <code>null</code> if it has not
+     * been initialized.
+     */
+    private String id;
+
+    /**
+     * The value held by this state; may be anything at all.
+     */
+    private Object value;
+
+    /**
+     * Adds a listener to changes for this state.
+     *
+     * @param listener
+     *            The listener to add; must not be <code>null</code>.
+     */
+    public void addListener(IStateListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Disposes of this state. This allows the state to unregister itself with
+     * any managers or as a listener.
+     */
+    public void dispose() {
+        // The default implementation does nothing.
+    }
+
+    /**
+     * Notifies listeners to this state that it has changed in some way.
+     *
+     * @param oldValue
+     *            The old value; may be anything.
+     */
+    protected final void fireStateChanged(Object oldValue) {
+        final Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IStateListener listener = cast(IStateListener) listeners[i];
+            listener.handleStateChange(this, oldValue);
+        }
+    }
+
+    /**
+     * Returns the identifier for this state.
+     *
+     * @return The id; may be <code>null</code>.
+     */
+    public final String getId() {
+        return id;
+    }
+
+    /**
+     * The current value associated with this state. This can be any type of
+     * object, but implementations will usually restrict this value to a
+     * particular type.
+     *
+     * @return The current value; may be anything.
+     */
+
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Removes a listener to changes from this state.
+     *
+     * @param listener
+     *            The listener to remove; must not be <code>null</code>.
+     */
+
+    public void removeListener(IStateListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Sets the identifier for this object.  This method should only be called
+     * by the command framework.  Clients should not call this method.
+     *
+     * @param id
+     *            The id; must not be <code>null</code>.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Sets the value for this state object.
+     *
+     * @param value
+     *            The value to set; may be anything.
+     */
+    public void setValue(Object value) {
+        if (!Util.equals(this.value, value)) {
+            final Object oldValue = this.value;
+            this.value = value;
+            fireStateChanged(oldValue);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/AbstractBitSetEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.common.AbstractBitSetEvent;
+
+/**
+ * <p>
+ * An event that carries with it two or more boolean values.  This provides a
+ * single integer value which can then be used as a bit set.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractBitSetEvent {
+
+    /**
+     * A collection of bits representing whether certain values have changed. A
+     * bit is set (i.e., <code>1</code>) if the corresponding property has
+     * changed. It can be assumed that this value will be correctly initialized
+     * by the superconstructor.
+     */
+    protected int changedValues = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/AbstractHandleObjectEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.common.AbstractHandleObjectEvent;
+
+import dwtx.core.commands.common.AbstractBitSetEvent;
+
+/**
+ * <p>
+ * An event fired from a <code>NamedHandleObject</code>. This provides
+ * notification of changes to the defined state, the name and the description.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class AbstractHandleObjectEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the category has changed its defined
+     * state.
+     */
+    protected static const int CHANGED_DEFINED = 1;
+
+    /**
+     * The last used bit so that subclasses can add more properties.
+     */
+    protected static const int LAST_BIT_USED_ABSTRACT_HANDLE = CHANGED_DEFINED;
+
+    /**
+     * Constructs a new instance of <code>AbstractHandleObjectEvent</code>.
+     *
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     */
+    protected this( bool definedChanged) {
+        if (definedChanged) {
+            changedValues |= CHANGED_DEFINED;
+        }
+    }
+
+    /**
+     * Returns whether or not the defined property changed.
+     *
+     * @return <code>true</code>, iff the defined property changed.
+     */
+    public final bool isDefinedChanged() {
+        return ((changedValues & CHANGED_DEFINED) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/AbstractNamedHandleEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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
+ ******************************************************************************/
+
+module dwtx.core.commands.common.AbstractNamedHandleEvent;
+
+import dwtx.core.commands.common.AbstractHandleObjectEvent;
+
+/**
+ * <p>
+ * An event fired from a <code>NamedHandleObject</code>. This provides
+ * notification of changes to the defined state, the name and the description.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class AbstractNamedHandleEvent :
+        AbstractHandleObjectEvent {
+
+    /**
+     * The bit used to represent whether the category has changed its
+     * description.
+     */
+    protected static const int CHANGED_DESCRIPTION = 1 << LAST_BIT_USED_ABSTRACT_HANDLE;
+
+    /**
+     * The bit used to represent whether the category has changed its name.
+     */
+    protected static const int CHANGED_NAME = 1 << LAST_BIT_USED_ABSTRACT_HANDLE;
+
+    /**
+     * The last used bit so that subclasses can add more properties.
+     */
+    protected static const int LAST_USED_BIT = CHANGED_NAME;
+
+    /**
+     * Constructs a new instance of <code>AbstractHandleObjectEvent</code>.
+     *
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     */
+    protected this(bool definedChanged,
+            bool descriptionChanged, bool nameChanged) {
+        super(definedChanged);
+
+        if (descriptionChanged) {
+            changedValues |= CHANGED_DESCRIPTION;
+        }
+        if (nameChanged) {
+            changedValues |= CHANGED_NAME;
+        }
+    }
+
+    /**
+     * Returns whether or not the description property changed.
+     *
+     * @return <code>true</code>, iff the description property changed.
+     */
+    public final bool isDescriptionChanged() {
+        return ((changedValues & CHANGED_DESCRIPTION) !is 0);
+    }
+
+    /**
+     * Returns whether or not the name property changed.
+     *
+     * @return <code>true</code>, iff the name property changed.
+     */
+    public final bool isNameChanged() {
+        return ((changedValues & CHANGED_NAME) !is 0);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/CommandException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.common.CommandException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an exception occured within the command architecture.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class CommandException : Exception {
+
+    /**
+     * This member variable is required here to allow us to compile against JCL
+     * foundation libraries.  The value may be <code>null</code>.
+     */
+    private Exception cause;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new instance of this class with the specified detail message
+     * and cause.
+     *
+     * @param message
+     *            the detail message; may be <code>null</code>.
+     * @param cause
+     *            the cause; may be <code>null</code>.
+     */
+    public this(String message, Exception cause) {
+        super(message);
+        // don't pass the cause to super, to allow compilation against JCL Foundation
+        this.cause = cause;
+    }
+
+    /**
+     * Returns the cause of this throwable or <code>null</code> if the
+     * cause is nonexistent or unknown.
+     *
+     * @return the cause or <code>null</code>
+     */
+    public Exception getCause() {
+        return cause;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/EventManager.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.common.EventManager;
+
+import dwtx.core.runtime.ListenerList;
+
+/**
+ * <p>
+ * A manager to which listeners can be attached. This handles the management of
+ * a list of listeners -- optimizing memory and performance. All the methods on
+ * this class are guaranteed to be thread-safe.
+ * </p>
+ * <p>
+ * Clients may extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class EventManager {
+
+    /**
+     * An empty array that can be returned from a call to
+     * {@link #getListeners()} when {@link #listenerList} is <code>null</code>.
+     */
+    private static const Object[] EMPTY_ARRAY = null;
+
+    /**
+     * A collection of objects listening to changes to this manager. This
+     * collection is <code>null</code> if there are no listeners.
+     */
+    private /+transient+/ ListenerList listenerList = null;
+
+    /**
+     * Adds a listener to this manager that will be notified when this manager's
+     * state changes.
+     *
+     * @param listener
+     *            The listener to be added; must not be <code>null</code>.
+     */
+    protected synchronized final void addListenerObject(Object listener) {
+        if (listenerList is null) {
+            listenerList = new ListenerList(ListenerList.IDENTITY);
+        }
+
+        listenerList.add(listener);
+    }
+
+    /**
+     * Clears all of the listeners from the listener list.
+     */
+    protected synchronized final void clearListeners() {
+        if (listenerList !is null) {
+            listenerList.clear();
+        }
+    }
+
+    /**
+     * Returns the listeners attached to this event manager.
+     *
+     * @return The listeners currently attached; may be empty, but never
+     *         <code>null</code>
+     */
+    protected final Object[] getListeners() {
+        ListenerList list = listenerList;
+        if (list is null) {
+            return EMPTY_ARRAY;
+        }
+
+        return list.getListeners();
+    }
+
+    /**
+     * Whether one or more listeners are attached to the manager.
+     *
+     * @return <code>true</code> if listeners are attached to the manager;
+     *         <code>false</code> otherwise.
+     */
+    protected final bool isListenerAttached() {
+        return listenerList !is null;
+    }
+
+    /**
+     * Removes a listener from this manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    protected synchronized final void removeListenerObject(Object listener) {
+        if (listenerList !is null) {
+            listenerList.remove(listener);
+
+            if (listenerList.isEmpty()) {
+                listenerList = null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/HandleObject.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.common.HandleObject;
+
+import dwtx.core.internal.commands.util.Util;
+import dwtx.core.commands.common.IIdentifiable;
+import dwtx.core.commands.common.EventManager;
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An object that can exist in one of two states: defined and undefined. This is
+ * used by APIs that want to give a handle to an object, even though the object
+ * does not fully exist yet. This way, users can attach listeners to objects
+ * before they come into existence. It also protects the API from users that do
+ * not release references when they should.
+ * </p>
+ * <p>
+ * To enforce good coding practice, all handle objects must implement
+ * <code>equals</code> and <code>toString</code>. Please use
+ * <code>string</code> to cache the result for <code>toString</code> once
+ * calculated.
+ * </p>
+ * <p>
+ * All handle objects are referred to using a single identifier. This identifier
+ * is a instance of <code>String</code>. It is important that this identifier
+ * remain unique within whatever context that handle object is being used. For
+ * example, there should only ever be one instance of <code>Command</code>
+ * with a given identifier.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class HandleObject : EventManager,
+        IIdentifiable {
+
+    /**
+     * The constant integer hash code value meaning the hash code has not yet
+     * been computed.
+     */
+    private static const int HASH_CODE_NOT_COMPUTED = -1;
+
+    /**
+     * A factor for computing the hash code for all schemes.
+     */
+    private static const int HASH_FACTOR = 89;
+
+    /**
+     * The seed for the hash code for all schemes.
+     */
+    private static const int HASH_INITIAL;
+
+    static this(){
+        HASH_INITIAL =
+        //typeid(char[]).getHash(& HandleObject.classinfo.name );
+        dwt.dwthelper.utils.toHash(HandleObject.classinfo.name);
+    }
+    /**
+     * Whether this object is defined. A defined object is one that has been
+     * fully initialized. By default, all objects start as undefined.
+     */
+    protected /+transient+/ bool defined = false;
+
+    /**
+     * The hash code for this object. This value is computed lazily, and marked
+     * as invalid when one of the values on which it is based changes.
+     */
+    private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+    /**
+     * The identifier for this object. This identifier should be unique across
+     * all objects of the same type and should never change. This value will
+     * never be <code>null</code>.
+     */
+    protected /+final+/ String id;
+
+    /**
+     * The string representation of this object. This string is for debugging
+     * purposes only, and is not meant to be displayed to the user. This value
+     * is computed lazily, and is cleared if one of its dependent values
+     * changes.
+     */
+    protected /+transient+/ String string = null;
+
+    /**
+     * Constructs a new instance of <code>HandleObject</code>.
+     *
+     * @param id
+     *            The id of this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        if (id is null) {
+            throw new NullPointerException(
+                    "Cannot create a handle with a null id"); //$NON-NLS-1$
+        }
+
+        this.id = id;
+    }
+
+    /**
+     * Tests whether this object is equal to another object. A handle object is
+     * only equal to another handle object with the same id and the same class.
+     *
+     * @param object
+     *            The object with which to compare; may be <code>null</code>.
+     * @return <code>true</code> if the objects are equal; <code>false</code>
+     *         otherwise.
+     */
+    public override int opEquals(Object object) {
+        // Check if they're the same.
+        if (object is this) {
+            return true;
+        }
+
+        // Check if they're the same type.
+        if (!(cast(HandleObject)object)) {
+            return false;
+        }
+
+        // Check each property in turn.
+        final HandleObject handle= cast(HandleObject) object;
+        return Util.equals(id, handle.id)
+                && (this.classinfo is handle.classinfo);
+    }
+
+    public override final String getId() {
+        return id;
+    }
+
+    /**
+     * Computes the hash code for this object based on the id.
+     *
+     * @return The hash code for this object.
+     */
+    public final override hash_t toHash() {
+        if (hashCode is HASH_CODE_NOT_COMPUTED) {
+            hashCode = HASH_INITIAL * HASH_FACTOR + Util.toHash(id);
+            if (hashCode is HASH_CODE_NOT_COMPUTED) {
+                hashCode++;
+            }
+        }
+        return hashCode;
+    }
+
+    /**
+     * Whether this instance is defined. A defined instance is one that has been
+     * fully initialized. This allows objects to effectively disappear even
+     * though other objects may still have references to them.
+     *
+     * @return <code>true</code> if this object is defined; <code>false</code>
+     *         otherwise.
+     */
+    public final bool isDefined() {
+        return defined;
+    }
+
+    /**
+     * The string representation of this object -- for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation; never <code>null</code>.
+     */
+    public override abstract String toString();
+
+    /**
+     * Makes this object becomes undefined. This method should make any defined
+     * properties <code>null</code>. It should also send notification to any
+     * listeners that these properties have changed.
+     */
+    public abstract void undefine();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/HandleObjectManager.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.common.HandleObjectManager;
+
+import dwtx.core.commands.common.HandleObject;
+import dwtx.core.commands.common.EventManager;
+import dwt.dwthelper.utils;
+
+import tango.util.collection.HashSet;
+import tango.util.collection.HashMap;
+import tango.util.collection.model.Set;
+import tango.util.collection.model.Map;
+
+// import java.util.HashMap;
+// import java.util.HashSet;
+// import java.util.Iterator;
+// import java.util.Map;
+// import java.util.Set;
+
+/**
+ * <p>
+ * A manager of {@link HandleObject} instances. This has some common behaviour
+ * which is shared between all such managers.
+ * </p>
+ * <p>
+ * Clients may extend.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class HandleObjectManager : EventManager {
+
+    /**
+     * The set of handle objects that are defined. This value may be empty, but
+     * it is never <code>null</code>.
+     */
+    protected const Set!(Object) definedHandleObjects;
+
+    /**
+     * The map of identifiers (<code>String</code>) to handle objects (
+     * <code>HandleObject</code>). This collection may be empty, but it is
+     * never <code>null</code>.
+     */
+    protected const Map!( String, Object ) handleObjectsById;
+
+    public this(){
+        definedHandleObjects = new HashSet!(Object)();
+        handleObjectsById = new HashMap!( String, Object )();
+    }
+
+    /**
+     * Verifies that the identifier is valid. Exceptions will be thrown if the
+     * identifier is invalid in some way.
+     *
+     * @param id
+     *            The identifier to validate; may be anything.
+     */
+    protected final void checkId(String id) {
+        if (id is null) {
+            throw new NullPointerException(
+                    "A handle object may not have a null identifier"); //$NON-NLS-1$
+        }
+
+        if (id.length < 1) {
+            throw new IllegalArgumentException(
+                    "The handle object must not have a zero-length identifier"); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Returns the set of identifiers for those handle objects that are defined.
+     *
+     * @return The set of defined handle object identifiers; this value may be
+     *         empty, but it is never <code>null</code>.
+     */
+    protected final HashSet!(String) getDefinedHandleObjectIds() {
+        HashSet!(String) definedHandleObjectIds = new HashSet!(String)(/+definedHandleObjects
+                .size()+/);
+        foreach ( v; definedHandleObjects.elements() ) {
+            auto handleObject = cast(HandleObject)v;
+            String id = handleObject.getId();
+            definedHandleObjectIds.add(id);
+        }
+        return definedHandleObjectIds;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/IIdentifiable.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,1 @@
+/*******************************************************************************
 * 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
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 ******************************************************************************/

module dwtx.core.commands.common.IIdentifiable;
import dwt.dwthelper.utils;
/**
 * <p>
 * An object that is unique identifiable based on the combination of its class
 * and its identifier.
 * </p>
 *
 * @see HandleObject
 * @since 3.2
 */
public interface IIdentifiable {

    /**
     * Returns the identifier for this object.
     *
     * @return The identifier; never <code>null</code>.
     */
   String getId();
}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/NamedHandleObject.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.common.NamedHandleObject;
+
+import dwtx.core.commands.common.HandleObject;
+import dwtx.core.commands.common.NotDefinedException;
+import dwt.dwthelper.utils;
+
+/**
+ * A handle object that carries with it a name and a description. This type of
+ * handle object is quite common across the commands code base. For example,
+ * <code>Command</code>, <code>Context</code> and <code>Scheme</code>.
+ *
+ * @since 3.1
+ */
+public abstract class NamedHandleObject : HandleObject {
+
+    /**
+     * The description for this handle. This value may be <code>null</code> if
+     * the handle is undefined or has no description.
+     */
+    protected String description = null;
+
+    /**
+     * The name of this handle. This valud should not be <code>null</code>
+     * unless the handle is undefined.
+     */
+    protected String name = null;
+
+    /**
+     * Constructs a new instance of <code>NamedHandleObject</code>.
+     *
+     * @param id
+     *            The identifier for this handle; must not be <code>null</code>.
+     */
+    protected this(String id) {
+        super(id);
+    }
+
+    /**
+     * Returns the description for this handle.
+     *
+     * @return The description; may be <code>null</code> if there is no
+     *         description.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public String getDescription() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get a description from an undefined object. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return description;
+    }
+
+    /**
+     * Returns the name for this handle.
+     *
+     * @return The name for this handle; never <code>null</code>.
+     * @throws NotDefinedException
+     *             If the handle is not currently defined.
+     */
+    public String getName() {
+        if (!isDefined()) {
+            throw new NotDefinedException(
+                    "Cannot get the name from an undefined object. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/NamedHandleObjectComparator.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,1 @@
+/*******************************************************************************
 * Copyright (c) 2005 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 dwtx.core.commands.common.NamedHandleObjectComparator;

// import java.util.Comparator;
import dwtx.core.commands.common.NotDefinedException;
import dwtx.core.commands.common.NamedHandleObject;
import dwtx.core.internal.commands.util.Util;
import dwt.dwthelper.utils;

/**
 * Comparator for instances of <code>NamedHandleObject</code> for display to
 * an end user. The comparison is based on the name of the instances.
 *
 * @since 3.2
 */
public class NamedHandleObjectComparator /+: Comparator+/ {

    /**
     * Compares to instances of NamedHandleObject based on their names. This is
     * useful is they are display to an end user.
     *
     * @param left
     *            The first obect to compare; may be <code>null</code>.
     * @param right
     *            The second object to compare; may be <code>null</code>.
     * @return <code>-1</code> if <code>left</code> is <code>null</code>
     *         and <code>right</code> is not <code>null</code>;
     *         <code>0</code> if they are both <code>null</code>;
     *         <code>1</code> if <code>left</code> is not <code>null</code>
     *         and <code>right</code> is <code>null</code>. Otherwise, the
     *         result of <code>left.compareTo(right)</code>.
     */
    public final int compare(Object left, Object right) {
        NamedHandleObject a = cast(NamedHandleObject) left;
        NamedHandleObject b = cast(NamedHandleObject) right;

        String aName = null;
        try {
            aName = a.getName();
        } catch (NotDefinedException e) {
            // Leave aName as null.
        }
        String bName = null;
        try {
            bName = b.getName();
        } catch (NotDefinedException e) {
            // Leave bName as null.
        }

        return Util.compare(aName, bName);
    }
}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/common/NotDefinedException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 dwtx.core.commands.common.NotDefinedException;
+
+import dwtx.core.commands.common.CommandException;
+import dwt.dwthelper.utils;
+
+/**
+ * Signals that an attempt was made to access the properties of an undefined
+ * object.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class NotDefinedException : CommandException {
+
+    /**
+     * Generated serial version UID for this class.
+     *
+     * @since 3.1
+     */
+    private static final long serialVersionUID = 3257572788998124596L;
+
+    /**
+     * Creates a new instance of this class with the specified detail message.
+     *
+     * @param s
+     *            the detail message; may be <code>null</code>.
+     */
+    public this(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/Context.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.contexts.Context;
+import tango.util.collection.HashSet;
+// import tango.util.Iterator;
+import tango.util.collection.model.Set;
+
+import dwtx.core.commands.contexts.ContextEvent;
+import dwtx.core.commands.contexts.IContextListener;
+import dwtx.core.commands.common.NamedHandleObject;
+import dwtx.core.commands.common.NotDefinedException;
+import dwtx.core.internal.commands.util.Util;
+
+import dwt.dwthelper.utils;
+
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * A context is an answer to the question "when". Other services can listen for
+ * the activation and deactivation of contexts, and change their own state in
+ * response to these changes. For example, Eclipse's key binding service listens
+ * to context activation and deactivation to determine which key bindings should
+ * be active.
+ * </p>
+ * <p>
+ * An instance of this interface can be obtained from an instance of
+ * <code>ContextManager</code> for any identifier, whether or not an context
+ * with that identifier is defined in the extension registry.
+ * </p>
+ * <p>
+ * The handle-based nature of this API allows it to work well with runtime
+ * plugin activation and deactivation. If a context is defined, that means that
+ * its corresponding plug-in is active. If the plug-in is then deactivated, the
+ * context will still exist but it will be undefined. An attempts to use an
+ * undefined context will result in a <code>NotDefinedException</code> being
+ * thrown.
+ * </p>
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ContextManager
+ */
+public final class Context : NamedHandleObject, Comparable {
+
+    /**
+     * The collection of all objects listening to changes on this context. This
+     * value is <code>null</code> if there are no listeners.
+     */
+    private Set!(IContextListener) listeners = null;
+
+    /**
+     * The parent identifier for this context. The meaning of a parent is
+     * dependent on the system using contexts. This value can be
+     * <code>null</code> if the context has no parent.
+     */
+    private String parentId = null;
+
+    /**
+     * Constructs a new instance of <code>Context</code>.
+     *
+     * @param id
+     *            The id for this context; must not be <code>null</code>.
+     */
+    this(String id) {
+        super(id);
+    }
+
+    /**
+     * Registers an instance of <code>IContextListener</code> to listen for
+     * changes to properties of this instance.
+     *
+     * @param listener
+     *            the instance to register. Must not be <code>null</code>. If
+     *            an attempt is made to register an instance which is already
+     *            registered with this instance, no operation is performed.
+     */
+    public final void addContextListener(IContextListener listener) {
+        if (listener is null) {
+            throw new NullPointerException();
+        }
+
+        if (listeners is null) {
+            listeners = new HashSet!(IContextListener);
+        }
+
+        listeners.add(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object object) {
+        Context scheme = cast(Context) object;
+        int compareTo = Util.compare(this.id, scheme.id);
+        if (compareTo is 0) {
+            compareTo = Util.compare(this.name, scheme.name);
+            if (compareTo is 0) {
+                compareTo = Util.compare(this.parentId, scheme.parentId);
+                if (compareTo is 0) {
+                    compareTo = Util.compare(this.description,
+                            scheme.description);
+                    if (compareTo is 0) {
+                        compareTo = Util.compare(this.defined, scheme.defined);
+                    }
+                }
+            }
+        }
+
+        return compareTo;
+    }
+
+    /**
+     * <p>
+     * Defines this context by giving it a name, and possibly a description and
+     * a parent identifier 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 context; must not be <code>null</code>.
+     * @param description
+     *            The description for this context; may be <code>null</code>.
+     * @param parentId
+     *            The parent identifier for this context; may be
+     *            <code>null</code>.
+     */
+    public final void define(String name, String description,
+            String parentId) {
+        if (name is null) {
+            throw new NullPointerException(
+                    "The name of a scheme 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 parentIdChanged = !Util.equals(this.parentId, parentId);
+        this.parentId = parentId;
+
+        fireContextChanged(new ContextEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+
+    /**
+     * Notifies all listeners that this context has changed. This sends the
+     * given event to all of the listeners, if any.
+     *
+     * @param event
+     *            The event to send to the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireContextChanged(ContextEvent event) {
+        if (event is null) {
+            throw new NullPointerException(
+                    "Cannot send a null event to listeners."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        foreach( listener; listeners){
+            listener.contextChanged(event);
+        }
+    }
+
+    /**
+     * Returns the identifier of the parent of this instance.
+     * <p>
+     * Notification is sent to all registered listeners if this property
+     * changes.
+     * </p>
+     *
+     * @return the identifier of the parent of this instance. May be
+     *         <code>null</code>.
+     * @throws NotDefinedException
+     *             if this instance is not defined.
+     */
+    public final String getParentId() {
+        if (!defined) {
+            throw new NotDefinedException(
+                    "Cannot get the parent identifier from an undefined context. " //$NON-NLS-1$
+                    ~ id);
+        }
+
+        return parentId;
+    }
+
+    /**
+     * Unregisters an instance of <code>IContextListener</code> listening for
+     * changes to properties of this instance.
+     *
+     * @param contextListener
+     *            the instance to unregister. Must not be <code>null</code>.
+     *            If an attempt is made to unregister an instance which is not
+     *            already registered with this instance, no operation is
+     *            performed.
+     */
+    public final void removeContextListener(
+            IContextListener contextListener) {
+        if (contextListener is null) {
+            throw new NullPointerException("Cannot remove a null listener."); //$NON-NLS-1$
+        }
+
+        if (listeners is null) {
+            return;
+        }
+
+        listeners.remove(contextListener);
+
+        if (listeners.drained()) {
+            listeners = null;
+        }
+    }
+
+    /**
+     * The string representation of this context -- 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) {
+            final StringBuffer stringBuffer = new StringBuffer();
+            stringBuffer.append("Context("); //$NON-NLS-1$
+            stringBuffer.append(id);
+            stringBuffer.append(',');
+            stringBuffer.append(name);
+            stringBuffer.append(',');
+            stringBuffer.append(description);
+            stringBuffer.append(',');
+            stringBuffer.append(parentId);
+            stringBuffer.append(',');
+            stringBuffer.append(defined ? "true" : "false");
+            stringBuffer.append(')');
+            string = stringBuffer.toString();
+        }
+        return string;
+    }
+
+    /**
+     * Makes this context become undefined. This has the side effect of changing
+     * the name, description and parent identifier to <code>null</code>.
+     * Notification is sent to all listeners.
+     */
+    public final void undefine() {
+        string = null;
+
+        bool definedChanged = defined;
+        defined = false;
+
+        bool nameChanged = name !is null;
+        name = null;
+
+        bool descriptionChanged = description !is null;
+        description = null;
+
+        bool parentIdChanged = parentId !is null;
+        parentId = null;
+
+        fireContextChanged(new ContextEvent(this, definedChanged, nameChanged,
+                descriptionChanged, parentIdChanged));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/ContextEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.contexts.ContextEvent;
+
+import dwtx.core.commands.contexts.Context;
+import dwtx.core.commands.common.AbstractNamedHandleEvent;
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * <code>IContext</code>.
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see IContextListener#contextChanged(ContextEvent)
+ */
+public final class ContextEvent : AbstractNamedHandleEvent {
+
+    /**
+     * The bit used to represent whether the context has changed its parent.
+     */
+    private static const int CHANGED_PARENT_ID = LAST_USED_BIT << 1;
+
+    /**
+     * The context that has changed. This value is never <code>null</code>.
+     */
+    private Context context;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param context
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param definedChanged
+     *            <code>true</code>, iff the defined property changed.
+     * @param nameChanged
+     *            <code>true</code>, iff the name property changed.
+     * @param descriptionChanged
+     *            <code>true</code>, iff the description property changed.
+     * @param parentIdChanged
+     *            <code>true</code>, iff the parentId property changed.
+     */
+    public this(Context context, bool definedChanged,
+            bool nameChanged, bool descriptionChanged,
+            bool parentIdChanged) {
+        super(definedChanged, descriptionChanged, nameChanged);
+
+        if (context is null) {
+            throw new NullPointerException();
+        }
+        this.context = context;
+
+        if (parentIdChanged) {
+            changedValues |= CHANGED_PARENT_ID;
+        }
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final Context getContext() {
+        return context;
+    }
+
+    /**
+     * Returns whether or not the parentId property changed.
+     *
+     * @return <code>true</code>, iff the parentId property changed.
+     */
+    public final bool isParentIdChanged() {
+        return ((changedValues & CHANGED_PARENT_ID) !is 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/ContextManager.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 dwtx.core.commands.contexts.ContextManager;
+
+import tango.util.collection.HashSet;
+import tango.util.collection.model.Set;
+import tango.util.collection.model.SetView;
+
+import dwtx.core.commands.contexts.IContextListener;
+import dwtx.core.commands.contexts.IContextManagerListener;
+import dwtx.core.commands.contexts.ContextEvent;
+import dwtx.core.commands.contexts.ContextManagerEvent;
+import dwtx.core.commands.contexts.Context;
+import dwtx.core.commands.common.HandleObjectManager;
+import dwtx.core.commands.util.Tracing;
+import dwtx.core.internal.commands.util.Util;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A context manager tracks the sets of defined and enabled contexts within the
+ * application. The manager sends notification events to listeners when these
+ * sets change. It is also possible to retrieve any given context with its
+ * identifier.
+ * </p>
+ * <p>
+ * This class is not intended to be extended by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ContextManager : HandleObjectManager,
+        IContextListener {
+
+    private static const String DEFER_EVENTS = "dwtx.ui.internal.contexts.deferEvents"; //$NON-NLS-1$
+    private static const String SEND_EVENTS = "dwtx.ui.internal.contexts.sendEvents"; //$NON-NLS-1$
+
+    /**
+     * This flag can be set to <code>true</code> if the context manager should
+     * print information to <code>System.out</code> when certain boundary
+     * conditions occur.
+     */
+    public static bool DEBUG = false;
+
+    /**
+     * The set of active context identifiers. This value may be empty, but it is
+     * never <code>null</code>.
+     */
+    private Set!(String) activeContextIds;
+    private static Set!(String) EMPTY_SET;
+
+    // allow the ContextManager to send one event for a larger delta
+    private bool caching = false;
+
+    private int cachingRef = 0;
+
+    private bool activeContextsChange = false;
+
+    private Set!(String) oldIds = null;
+
+    public this(){
+        activeContextIds = new HashSet!(String);
+        if( EMPTY_SET is null ){
+            EMPTY_SET = new HashSet!(String);
+        }
+    }
+
+    /**
+     * Activates a context in this context manager.
+     *
+     * @param contextId
+     *            The identifier of the context to activate; must not be
+     *            <code>null</code>.
+     */
+    public final void addActiveContext(String contextId) {
+        if (DEFER_EVENTS.equals(contextId)) {
+            cachingRef++;
+            if (cachingRef is 1 ) {
+                setEventCaching(true);
+            }
+            return;
+        } else if (SEND_EVENTS.equals(contextId)) {
+            cachingRef--;
+            if (cachingRef is 0) {
+                setEventCaching(false);
+            }
+            return;
+        }
+
+        if (activeContextIds.contains(contextId)) {
+            return;
+        }
+        activeContextsChange = true;
+
+        if (caching) {
+            activeContextIds.add(contextId);
+        } else {
+            Set!(String) previouslyActiveContextIds = activeContextIds.duplicate();
+            activeContextIds.add(contextId);
+
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", SetToString(activeContextIds)); //$NON-NLS-1$
+        }
+
+    }
+
+    /**
+     * Adds a listener to this context manager. The listener will be notified
+     * when the set of defined contexts changes. This can be used to track the
+     * global appearance and disappearance of contexts.
+     *
+     * @param listener
+     *            The listener to attach; must not be <code>null</code>.
+     */
+    public final void addContextManagerListener(
+            IContextManagerListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    public final void contextChanged(ContextEvent contextEvent) {
+        if (contextEvent.isDefinedChanged()) {
+            Context context = contextEvent.getContext();
+            String contextId = context.getId();
+            bool contextIdAdded = context.isDefined();
+            if (contextIdAdded) {
+                definedHandleObjects.add(context);
+            } else {
+                definedHandleObjects.remove(context);
+            }
+            if (isListenerAttached()) {
+                fireContextManagerChanged(new ContextManagerEvent(this,
+                        contextId, contextIdAdded, false, null));
+            }
+        }
+    }
+
+    /**
+     * Notifies all of the listeners to this manager that the set of defined
+     * context identifiers has changed.
+     *
+     * @param event
+     *            The event to send to all of the listeners; must not be
+     *            <code>null</code>.
+     */
+    private final void fireContextManagerChanged(ContextManagerEvent event) {
+        if (event is null) {
+            throw new NullPointerException();
+        }
+
+        Object[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            IContextManagerListener listener = cast(IContextManagerListener) listeners[i];
+            listener.contextManagerChanged(event);
+        }
+    }
+
+    /**
+     * Returns the set of active context identifiers.
+     *
+     * @return The set of active context identifiers; this value may be
+     *         <code>null</code> if no active contexts have been set yet. If
+     *         the set is not <code>null</code>, then it contains only
+     *         instances of <code>String</code>.
+     */
+    public final SetView!(String) getActiveContextIds() {
+        return /+Collections.unmodifiableSet(+/activeContextIds/+)+/;
+    }
+
+    /**
+     * Gets the context with the given identifier. If no such context currently
+     * exists, then the context will be created (but be undefined).
+     *
+     * @param contextId
+     *            The identifier to find; must not be <code>null</code>.
+     * @return The context with the given identifier; this value will never be
+     *         <code>null</code>, but it might be undefined.
+     * @see Context
+     */
+    public final Context getContext(String contextId) {
+        checkId(contextId);
+
+        Context context = cast(Context) handleObjectsById.get(contextId);
+        if (context is null) {
+            context = new Context(contextId);
+            handleObjectsById.add(contextId, context);
+            context.addContextListener(this);
+        }
+
+        return context;
+    }
+
+    /**
+     * Returns the set of identifiers for those contexts that are defined.
+     *
+     * @return The set of defined context identifiers; this value may be empty,
+     *         but it is never <code>null</code>.
+     */
+    public final Set!(String) getDefinedContextIds() {
+        return getDefinedHandleObjectIds();
+    }
+
+    /**
+     * Returns the those contexts that are defined.
+     *
+     * @return The defined contexts; this value may be empty, but it is never
+     *         <code>null</code>.
+     * @since 3.2
+     */
+    public final Context[] getDefinedContexts() {
+        return cast(Context[]) definedHandleObjects
+                .toArray(/+new Context[definedHandleObjects.size()]+/);
+    }
+
+    /**
+     * Deactivates a context in this context manager.
+     *
+     * @param contextId
+     *            The identifier of the context to deactivate; must not be
+     *            <code>null</code>.
+     */
+    public final void removeActiveContext(String contextId) {
+        if (!activeContextIds.contains(contextId)) {
+            return;
+        }
+
+        activeContextsChange = true;
+        if (caching) {
+            activeContextIds.remove(contextId);
+        } else {
+            auto previouslyActiveContextIds = activeContextIds.dup;
+            activeContextIds.remove(contextId);
+
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", SetToString(activeContextIds)); //$NON-NLS-1$
+        }
+    }
+    private String SetToString( Set!(String) set ){
+        String s = "[";
+        foreach( id; set ){
+            if( s.length > 1 ) s ~= ", ";
+            s ~= id;
+        }
+        s ~= "]";
+        return s;
+    }
+    /**
+     * Removes a listener from this context manager.
+     *
+     * @param listener
+     *            The listener to be removed; must not be <code>null</code>.
+     */
+    public final void removeContextManagerListener(
+            IContextManagerListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Changes the set of active contexts for this context manager. The whole
+     * set is required so that internal consistency can be maintained and so
+     * that excessive recomputations do nothing occur.
+     *
+     * @param activeContextIds
+     *            The new set of active context identifiers; may be
+     *            <code>null</code>.
+     */
+    public final void setActiveContextIds(Set!(String) activeContextIds) {
+        if (Util.equals(this.activeContextIds.toArray, activeContextIds.toArray)) {
+            return;
+        }
+
+        activeContextsChange = true;
+
+        Set!(String) previouslyActiveContextIds = this.activeContextIds;
+        if (activeContextIds !is null) {
+            this.activeContextIds = activeContextIds.duplicate;
+        } else {
+            this.activeContextIds = null;
+        }
+
+        if (DEBUG) {
+            Tracing.printTrace("CONTEXTS", (activeContextIds is null) ? "none" //$NON-NLS-1$ //$NON-NLS-2$
+                    : SetToString(activeContextIds));
+        }
+
+        if (!caching) {
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, previouslyActiveContextIds));
+        }
+    }
+
+    /**
+     * Set the manager to cache context id changes.
+     *
+     * @param cache
+     *            <code>true</code> to turn caching on, <code>false</code>
+     *            to turn caching off and send an event if necessary.
+     * @since 3.3
+     */
+    private void setEventCaching(bool cache) {
+        if (caching is cache) {
+            return;
+        }
+        caching = cache;
+        bool fireChange = activeContextsChange;
+        Set!(String) holdOldIds = (oldIds is null? EMPTY_SET : oldIds );
+
+        if (caching) {
+            oldIds = activeContextIds.duplicate;
+        } else {
+            oldIds = null;
+        }
+        activeContextsChange = false;
+
+        if (!caching && fireChange) {
+            fireContextManagerChanged(new ContextManagerEvent(this, null,
+                    false, true, holdOldIds));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/ContextManagerEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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 dwtx.core.commands.contexts.ContextManagerEvent;
+import tango.util.collection.model.Set;
+
+import dwtx.core.commands.contexts.ContextManager;
+import dwtx.core.commands.common.AbstractBitSetEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An event indicating that the set of defined context identifiers has changed.
+ * </p>
+ *
+ * @since 3.1
+ * @see IContextManagerListener#contextManagerChanged(ContextManagerEvent)
+ */
+public final class ContextManagerEvent : AbstractBitSetEvent {
+
+    /**
+     * The bit used to represent whether the set of defined contexts has
+     * changed.
+     */
+    private static const int CHANGED_CONTEXT_DEFINED = 1 << 1;
+
+    /**
+     * The bit used to represent whether the set of active contexts has changed.
+     */
+    private static const int CHANGED_CONTEXTS_ACTIVE = 1;
+
+    /**
+     * The context identifier that was added or removed from the list of defined
+     * context identifiers.
+     */
+    private const String contextId;
+
+    /**
+     * The context manager that has changed.
+     */
+    private const ContextManager contextManager;
+
+    /**
+     * The set of context identifiers (strings) that were active before the
+     * change occurred. If the active contexts did not changed, then this value
+     * is <code>null</code>.
+     */
+    private const Set!(String) previouslyActiveContextIds;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param contextManager
+     *            the instance of the interface that changed; must not be
+     *            <code>null</code>.
+     * @param contextId
+     *            The context identifier that was added or removed; may be
+     *            <code>null</code> if the active contexts are changing.
+     * @param contextIdAdded
+     *            Whether the context identifier became defined (otherwise, it
+     *            became undefined).
+     * @param activeContextsChanged
+     *            Whether the list of active contexts has changed.
+     * @param previouslyActiveContextIds
+     *            the set of identifiers of previously active contexts. This set
+     *            may be empty. If this set is not empty, it must only contain
+     *            instances of <code>String</code>. This set must be
+     *            <code>null</code> if activeContextChanged is
+     *            <code>false</code> and must not be null if
+     *            activeContextChanged is <code>true</code>.
+     */
+    public this(ContextManager contextManager,
+            String contextId, bool contextIdAdded,
+            bool activeContextsChanged,
+            Set!(String) previouslyActiveContextIds) {
+        if (contextManager is null) {
+            throw new NullPointerException();
+        }
+
+        this.contextManager = contextManager;
+        this.contextId = contextId;
+        this.previouslyActiveContextIds = previouslyActiveContextIds;
+
+        if (contextIdAdded) {
+            changedValues |= CHANGED_CONTEXT_DEFINED;
+        }
+        if (activeContextsChanged) {
+            changedValues |= CHANGED_CONTEXTS_ACTIVE;
+        }
+    }
+
+    /**
+     * Returns the context identifier that was added or removed.
+     *
+     * @return The context identifier that was added or removed. This value may
+     *         be <code>null</code> if no context identifier was added or
+     *         removed.
+     */
+    public final String getContextId() {
+        return contextId;
+    }
+
+    /**
+     * Returns the instance of the interface that changed.
+     *
+     * @return the instance of the interface that changed. Guaranteed not to be
+     *         <code>null</code>.
+     */
+    public final ContextManager getContextManager() {
+        return contextManager;
+    }
+
+    /**
+     * Returns the set of identifiers to previously active contexts.
+     *
+     * @return the set of identifiers to previously active contexts. This set
+     *         may be empty. If this set is not empty, it is guaranteed to only
+     *         contain instances of <code>String</code>. This set is
+     *         guaranteed to be <code>null</code> if
+     *         haveActiveContextChanged() is <code>false</code> and is
+     *         guaranteed to not be <code>null</code> if
+     *         haveActiveContextsChanged() is <code>true</code>.
+     */
+    public final Set!(String) getPreviouslyActiveContextIds() {
+        return previouslyActiveContextIds;
+    }
+
+    /**
+     * Returns whether the active context identifiers have changed.
+     *
+     * @return <code>true</code> if the collection of active contexts changed;
+     *         <code>false</code> otherwise.
+     */
+    public final bool isActiveContextsChanged() {
+        return ((changedValues & CHANGED_CONTEXTS_ACTIVE) !is 0);
+    }
+
+    /**
+     * Returns whether the list of defined context identifiers has changed.
+     *
+     * @return <code>true</code> if the list of context identifiers has
+     *         changed; <code>false</code> otherwise.
+     */
+    public final bool isContextChanged() {
+        return (contextId !is null);
+    }
+
+    /**
+     * Returns whether the context identifier became defined. Otherwise, the
+     * context identifier became undefined.
+     *
+     * @return <code>true</code> if the context identifier became defined;
+     *         <code>false</code> if the context identifier became undefined.
+     */
+    public final bool isContextDefined() {
+        return (((changedValues & CHANGED_CONTEXT_DEFINED) !is 0) && (contextId !is null));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/IContextListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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 dwtx.core.commands.contexts.IContextListener;
+
+import dwtx.core.commands.contexts.ContextEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IContext</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see Context#addContextListener(IContextListener)
+ * @see Context#removeContextListener(IContextListener)
+ */
+public interface IContextListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IContext</code> have changed. Specific details are described in
+     * the <code>ContextEvent</code>.
+     *
+     * @param contextEvent
+     *            the context event. Guaranteed not to be <code>null</code>.
+     */
+    void contextChanged(ContextEvent contextEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/contexts/IContextManagerListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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 dwtx.core.commands.contexts.IContextManagerListener;
+
+import dwtx.core.commands.contexts.ContextManagerEvent;
+
+/**
+ * An instance of this interface can be used by clients to receive notification
+ * of changes to one or more instances of <code>IContextManager</code>.
+ * <p>
+ * This interface may be implemented by clients.
+ * </p>
+ *
+ * @since 3.1
+ * @see ContextManager#addContextManagerListener(IContextManagerListener)
+ * @see ContextManager#removeContextManagerListener(IContextManagerListener)
+ */
+public interface IContextManagerListener {
+
+    /**
+     * Notifies that one or more properties of an instance of
+     * <code>IContextManager</code> have changed. Specific details are
+     * described in the <code>ContextManagerEvent</code>.
+     *
+     * @param contextManagerEvent
+     *            the context manager event. Guaranteed not to be
+     *            <code>null</code>.
+     */
+    void contextManagerChanged(ContextManagerEvent contextManagerEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/AbstractOperation.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.AbstractOperation;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * Abstract implementation for an undoable operation. At a minimum, subclasses
+ * should implement behavior for
+ * {@link IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable)},
+ * {@link IUndoableOperation#redo(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable)},
+ * and
+ * {@link IUndoableOperation#undo(dwtx.core.runtime.IProgressMonitor, dwtx.core.runtime.IAdaptable)}.
+ * </p>
+ *
+ * @see dwtx.core.commands.operations.IUndoableOperation
+ *
+ * @since 3.1
+ */
+public abstract class AbstractOperation : IUndoableOperation {
+    Seq!(IUndoContext) contexts;
+
+    private String label = ""; //$NON-NLS-1$
+
+    /**
+     * Construct an operation that has the specified label.
+     *
+     * @param label
+     *            the label to be used for the operation.
+     */
+    public this(String label) {
+        this.label = label;
+        contexts = new ArraySeq!(IUndoContext);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#addContext(dwtx.core.commands.operations.IUndoContext)
+     *
+     * <p> Subclasses may override this method. </p>
+     */
+    public void addContext(IUndoContext context) {
+        if (!contexts.contains(context)) {
+            contexts.append(context);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canExecute()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     *
+     */
+    public bool canExecute() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canRedo()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public bool canRedo() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canUndo()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public bool canUndo() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#dispose()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public void dispose() {
+        // nothing to dispose.
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public abstract IStatus execute(IProgressMonitor monitor, IAdaptable info);
+
+    public final IUndoContext[] getContexts() {
+        return contexts.toArray();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#getLabel()
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Set the label of the operation to the specified name.
+     *
+     * @param name
+     *            the string to be used for the label.
+     */
+    public void setLabel(String name) {
+        label = name;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#hasContext(dwtx.core.commands.operations.IUndoContext)
+     */
+    public final bool hasContext(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        for (int i = 0; i < contexts.size(); i++) {
+            IUndoContext otherContext = 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;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#redo(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public abstract IStatus redo(IProgressMonitor monitor, IAdaptable info);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#removeContext(dwtx.core.commands.operations.IUndoContext)
+     *      <p> Default implementation. Subclasses may override this method.
+     *      </p>
+     */
+
+    public void removeContext(IUndoContext context) {
+        contexts.remove(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#undo(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public abstract IStatus undo(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * The string representation of this operation. Used for debugging purposes
+     * only. This string should not be shown to an end user.
+     *
+     * @return The string representation.
+     */
+    public String toString() {
+        StringBuffer stringBuffer = new StringBuffer();
+        stringBuffer.append(getLabel());
+        stringBuffer.append("("); //$NON-NLS-1$
+        IUndoContext[] contexts = getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            stringBuffer.append((cast(Object)contexts[i]).toString());
+            if (i !is contexts.length - 1) {
+                stringBuffer.append(',');
+            }
+        }
+        stringBuffer.append(')');
+        return stringBuffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/DefaultOperationHistory.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,1426 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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 dwtx.core.commands.operations.DefaultOperationHistory;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.HashMap;
+import tango.util.collection.model.Map;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.util.Tracing;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.ISafeRunnable;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.ListenerList;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.SafeRunner;
+import dwtx.core.runtime.Status;
+
+import dwtx.core.commands.operations.IOperationHistory;
+import dwtx.core.commands.operations.IUndoContext;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.ICompositeOperation;
+import dwtx.core.commands.operations.IOperationApprover;
+import dwtx.core.commands.operations.IOperationApprover2;
+import dwtx.core.commands.operations.IOperationHistoryListener;
+import dwtx.core.commands.operations.OperationHistoryEvent;
+import dwtx.core.commands.operations.IAdvancedUndoableOperation;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Integer;
+
+/**
+ * <p>
+ * A base implementation of IOperationHistory that implements a linear undo and
+ * redo model . The most recently added operation is available for undo, and the
+ * most recently undone operation is available for redo.
+ * </p>
+ * <p>
+ * If the operation eligible for undo is not in a state where it can be undone,
+ * then no undo is available. No other operations are considered. Likewise, if
+ * the operation available for redo cannot be redone, then no redo is available.
+ * </p>
+ * <p>
+ * Implementations for the direct undo and redo of a specified operation are
+ * available. If a strict linear undo is to be enforced, than an
+ * IOperationApprover should be installed that prevents undo and redo of any
+ * operation that is not the most recently undone or redone operation in all of
+ * its undo contexts.
+ * </p>
+ * <p>
+ * The data structures used by the DefaultOperationHistory are synchronized, and
+ * entry points that modify the undo and redo history concurrently are also
+ * synchronized. This means that the DefaultOperationHistory is relatively
+ * "thread-friendly" in its implementation. Outbound notifications or operation
+ * approval requests will occur on the thread that initiated the request.
+ * Clients may use DefaultOperationHistory API from any thread; however,
+ * listeners or operation approvers that receive notifications from the
+ * DefaultOperationHistory must be prepared to receive these notifications from
+ * a background thread. Any UI access occurring inside these notifications must
+ * be properly synchronized using the techniques specified by the client's
+ * widget library.
+ * </p>
+ *
+ * <p>
+ * This implementation is not intended to be subclassed.
+ * </p>
+ *
+ * @see dwtx.core.commands.operations.IOperationHistory
+ * @see dwtx.core.commands.operations.IOperationApprover
+ *
+ * @since 3.1
+ */
+public final class DefaultOperationHistory : IOperationHistory {
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever notifications about
+     * changes to the history occur. This flag should be used for debug purposes
+     * only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_NOTIFICATION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an unexpected condition
+     * arises. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_UNEXPECTED = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an undo context is
+     * disposed. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_DISPOSE = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> during the open/close sequence.
+     * This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_OPENOPERATION = false;
+
+    /**
+     * This flag can be set to <code>true</code> if the history should print
+     * information to <code>System.out</code> whenever an operation is not
+     * approved. This flag should be used for debug purposes only.
+     */
+    public static bool DEBUG_OPERATION_HISTORY_APPROVAL = false;
+
+    static const int DEFAULT_LIMIT = 20;
+
+    /**
+     * the list of {@link IOperationApprover}s
+     */
+    ListenerList approvers;
+
+    /**
+     * a map of undo limits per context
+     */
+    private Map!(IUndoContext,Integer) limits;
+
+    /**
+     * the list of {@link IOperationHistoryListener}s
+     */
+    ListenerList listeners;
+
+    /**
+     * the list of operations available for redo, LIFO
+     */
+    private ArraySeq!(IUndoableOperation) redoList;
+
+    /**
+     * the list of operations available for undo, LIFO
+     */
+    private ArraySeq!(IUndoableOperation) undoList;
+
+    /**
+     * a lock that is used to synchronize access between the undo and redo
+     * history
+     */
+    final Object undoRedoHistoryLock;
+
+    /**
+     * An operation that is "absorbing" all other operations while it is open.
+     * When this is not null, other operations added or executed are added to
+     * this composite.
+     *
+     */
+    private ICompositeOperation openComposite = null;
+
+    /**
+     * a lock that is used to synchronize access to the open composite.
+     */
+    const Object openCompositeLock;
+
+    /**
+     * Create an instance of DefaultOperationHistory.
+     */
+    public this() {
+        openCompositeLock = new Object();
+        undoRedoHistoryLock = new Object();
+        approvers = new ListenerList(ListenerList.IDENTITY);
+        limits = new HashMap!(IUndoContext,Integer);
+        listeners = new ListenerList(ListenerList.IDENTITY);
+        redoList = new ArraySeq!(IUndoableOperation);
+        undoList = new ArraySeq!(IUndoableOperation);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#add(dwtx.core.commands.operations.IUndoableOperation)
+     */
+    public void add(IUndoableOperation operation) {
+        Assert.isNotNull( cast(Object)operation);
+
+        /*
+         * If we are in the middle of executing an open batching operation, and
+         * this is not that operation, then we need only add the context of the
+         * new operation to the batch. The operation itself is disposed since we
+         * will never undo or redo it. We consider it to be triggered by the
+         * batching operation and assume that its undo will be triggered by the
+         * batching operation undo.
+         */
+        synchronized (openCompositeLock) {
+            if (openComposite !is null && openComposite !is operation) {
+                openComposite.add(operation);
+                return;
+            }
+        }
+
+        if (checkUndoLimit(operation)) {
+            synchronized (undoRedoHistoryLock) {
+                undoList.append(operation);
+            }
+            notifyAdd(operation);
+
+            // flush redo stack for related contexts
+            IUndoContext[] contexts = operation.getContexts();
+            for (int i = 0; i < contexts.length; i++) {
+                flushRedo(contexts[i]);
+            }
+        } else {
+            // Dispose the operation since we will not have a reference to it.
+            operation.dispose();
+        }
+    }
+
+    /**
+     * <p>
+     * Add the specified approver to the list of operation approvers consulted
+     * by the operation history before an undo or redo is allowed to proceed.
+     * This method has no effect if the instance being added is already in the
+     * list.
+     * </p>
+     * <p>
+     * Operation approvers must be prepared to receive these the operation
+     * approval messages from a background thread. Any UI access occurring
+     * inside the implementation must be properly synchronized using the
+     * techniques specified by the client's widget library.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be added as an approver.
+     *
+     */
+
+    public void addOperationApprover(IOperationApprover approver) {
+        approvers.add(cast(Object)approver);
+    }
+
+    /**
+     * <p>
+     * Add the specified listener to the list of operation history listeners
+     * that are notified about changes in the history or operations that are
+     * executed, undone, or redone. This method has no effect if the instance
+     * being added is already in the list.
+     * </p>
+     * <p>
+     * Operation history listeners must be prepared to receive notifications
+     * from a background thread. Any UI access occurring inside the
+     * implementation must be properly synchronized using the techniques
+     * specified by the client's widget library.
+     * </p>
+     *
+     * @param listener
+     *            the IOperationHistoryListener to be added as a listener.
+     *
+     * @see dwtx.core.commands.operations.IOperationHistoryListener
+     * @see dwtx.core.commands.operations.OperationHistoryEvent
+     */
+    public void addOperationHistoryListener(IOperationHistoryListener listener) {
+        listeners.add(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#canRedo(dwtx.core.commands.operations.IUndoContext)
+     */
+    public bool canRedo(IUndoContext context) {
+        // null context is allowed and passed through
+        IUndoableOperation operation = getRedoOperation(context);
+        return (operation !is null && operation.canRedo());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#canUndo(dwtx.core.commands.operations.IUndoContext)
+     */
+    public bool canUndo(IUndoContext context) {
+        // null context is allowed and passed through
+        IUndoableOperation operation = getUndoOperation(context);
+        return (operation !is null && operation.canUndo());
+    }
+
+    /**
+     * Check the redo limit before adding an operation. In theory the redo limit
+     * should never be reached, because the redo items are transferred from the
+     * undo history, which has the same limit. The redo history is cleared
+     * whenever a new operation is added. We check for completeness since
+     * implementations may change over time.
+     *
+     * Return a bool indicating whether the redo should proceed.
+     */
+    private bool checkRedoLimit(IUndoableOperation operation) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            int limit = getLimit(contexts[i]);
+            if (limit > 0) {
+                forceRedoLimit(contexts[i], limit - 1);
+            } else {
+                // this context has a 0 limit
+                operation.removeContext(contexts[i]);
+            }
+        }
+        return operation.getContexts().length > 0;
+    }
+
+    /**
+     * Check the undo limit before adding an operation. Return a bool
+     * indicating whether the undo should proceed.
+     */
+    private bool checkUndoLimit(IUndoableOperation operation) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            int limit = getLimit(contexts[i]);
+            if (limit > 0) {
+                forceUndoLimit(contexts[i], limit - 1);
+            } else {
+                // this context has a 0 limit
+                operation.removeContext(contexts[i]);
+            }
+        }
+        return operation.getContexts().length > 0;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#dispose(dwtx.core.commands.operations.IUndoContext,
+     *      bool, bool, bool)
+     */
+    public void dispose(IUndoContext context, bool flushUndo_,
+            bool flushRedo_, bool flushContext_) {
+        // dispose of any limit that was set for the context if it is not to be
+        // used again.
+        if (flushContext_) {
+            if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+                Tracing.printTrace("OPERATIONHISTORY", "Flushing context " //$NON-NLS-1$//$NON-NLS-2$
+                        ~ (cast(Object)context).toString );
+            }
+            flushUndo(context);
+            flushRedo(context);
+            synchronized(limits) limits.removeKey(context);
+            return;
+        }
+        if (flushUndo_) {
+            flushUndo(context);
+        }
+        if (flushRedo_) {
+            flushRedo(context);
+        }
+
+    }
+
+    /**
+     * Perform the redo. All validity checks have already occurred.
+     *
+     * @param monitor
+     * @param operation
+     */
+    private IStatus doRedo(IProgressMonitor monitor, IAdaptable info,
+            IUndoableOperation operation) {
+
+        IStatus status = getRedoApproval(operation, info);
+        if (status.isOK()) {
+            notifyAboutToRedo(operation);
+            try {
+                status = operation.redo(monitor, info);
+            } catch (OperationCanceledException e) {
+                status = Status.CANCEL_STATUS;
+            } catch (ExecutionException e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "ExecutionException while redoing " ~ (cast(Object)operation).toString ); //$NON-NLS-1$
+                }
+                throw e;
+            } catch (Exception e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Exception while redoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw new ExecutionException(
+                        "While redoing the operation, an exception occurred", e); //$NON-NLS-1$
+            }
+        }
+
+        // if successful, the operation is removed from the redo history and
+        // placed back in the undo history.
+        if (status.isOK()) {
+            bool addedToUndo = true;
+            synchronized (undoRedoHistoryLock) {
+                synchronized(redoList) redoList.remove(operation);
+                if (checkUndoLimit(operation)) {
+                    undoList.append(operation);
+                } else {
+                    addedToUndo = false;
+                }
+            }
+            // dispose the operation since we could not add it to the
+            // stack and will no longer have a reference to it.
+            if (!addedToUndo) {
+                operation.dispose();
+            }
+
+            // notify listeners must happen after history is updated
+            notifyRedone(operation);
+        } else {
+            notifyNotOK(operation, status);
+        }
+
+        return status;
+    }
+
+    /**
+     * Perform the undo. All validity checks have already occurred.
+     *
+     * @param monitor
+     * @param operation
+     */
+    private IStatus doUndo(IProgressMonitor monitor, IAdaptable info,
+            IUndoableOperation operation) {
+        IStatus status = getUndoApproval(operation, info);
+        if (status.isOK()) {
+            notifyAboutToUndo(operation);
+            try {
+                status = operation.undo(monitor, info);
+            } catch (OperationCanceledException e) {
+                status = Status.CANCEL_STATUS;
+            } catch (ExecutionException e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "ExecutionException while undoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw e;
+            } catch (Exception e) {
+                notifyNotOK(operation);
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Exception while undoing " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+                throw new ExecutionException(
+                        "While undoing the operation, an exception occurred", e); //$NON-NLS-1$
+            }
+        }
+        // if successful, the operation is removed from the undo history and
+        // placed in the redo history.
+        if (status.isOK()) {
+            bool addedToRedo = true;
+            synchronized (undoRedoHistoryLock) {
+                undoList.remove(operation);
+                if (checkRedoLimit(operation)) {
+                    synchronized(redoList) redoList.append(operation);
+                } else {
+                    addedToRedo = false;
+                }
+            }
+            // dispose the operation since we could not add it to the
+            // stack and will no longer have a reference to it.
+            if (!addedToRedo) {
+                operation.dispose();
+            }
+            // notification occurs after the undo and redo histories are
+            // adjusted
+            notifyUndone(operation);
+        } else {
+            notifyNotOK(operation, status);
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#execute(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus execute(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+
+        // error if operation is invalid
+        if (!operation.canExecute()) {
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        // check with the operation approvers
+        IStatus status = getExecuteApproval(operation, info);
+        if (!status.isOK()) {
+            // not approved. No notifications are sent, just return the status.
+            return status;
+        }
+
+        /*
+         * If we are in the middle of an open composite, then we will add this
+         * operation to the open operation rather than add the operation to the
+         * history. We will still execute it.
+         */
+        bool merging = false;
+        synchronized (openCompositeLock) {
+            if (openComposite !is null) {
+                // the composite shouldn't be executed explicitly while it is
+                // still
+                // open
+                if (openComposite is operation) {
+                    return IOperationHistory.OPERATION_INVALID_STATUS;
+                }
+                openComposite.add(operation);
+                merging = true;
+            }
+        }
+
+        /*
+         * Execute the operation
+         */
+        if (!merging) {
+            notifyAboutToExecute(operation);
+        }
+        try {
+            status = operation.execute(monitor, info);
+        } catch (OperationCanceledException e) {
+            status = Status.CANCEL_STATUS;
+        } catch (ExecutionException e) {
+            notifyNotOK(operation);
+            throw e;
+        } catch (Exception e) {
+            notifyNotOK(operation);
+            throw new ExecutionException(
+                    "While executing the operation, an exception occurred", e); //$NON-NLS-1$
+        }
+
+        // if successful, the notify listeners are notified and the operation is
+        // added to the history
+        if (!merging) {
+            if (status.isOK()) {
+                notifyDone(operation);
+                add(operation);
+            } else {
+                notifyNotOK(operation, status);
+                // dispose the operation since we did not add it to the stack
+                // and will no longer have a reference to it.
+                operation.dispose();
+            }
+        }
+        // all other severities are not interpreted. Simply return the status.
+        return status;
+    }
+
+    /*
+     * Filter the specified list to include only the specified undo context.
+     */
+    private IUndoableOperation[] filter(Seq!(IUndoableOperation) list, IUndoContext context) {
+        /*
+         * This method is used whenever there is a need to filter the undo or
+         * redo history on a particular context. Currently there are no caches
+         * kept to optimize repeated requests for the same filter. If benchmarks
+         * show this to be a common pattern that causes performances problems,
+         * we could implement a filtered cache here that is nullified whenever
+         * the global history changes.
+         */
+
+        auto filtered = new ArraySeq!(IUndoableOperation);
+        filtered.capacity(list.size());
+//         Iterator iterator = list.iterator();
+        synchronized (undoRedoHistoryLock) {
+            foreach( operation; list ){
+//             while (iterator.hasNext()) {
+//                 IUndoableOperation operation = (IUndoableOperation) iterator
+//                         .next();
+                if (operation.hasContext(context)) {
+                    filtered.append(operation);
+                }
+            }
+        }
+        return filtered.toArray();
+    }
+
+    /*
+     * Flush the redo stack of all operations that have the given context.
+     */
+    private void flushRedo(IUndoContext context) {
+        if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+            Tracing.printTrace("OPERATIONHISTORY", "Flushing redo history for " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)context).toString );
+        }
+
+        IUndoableOperation[] filtered = filter(redoList, context);
+        for (int i = 0; i < filtered.length; i++) {
+            IUndoableOperation operation = filtered[i];
+            if (context is GLOBAL_UNDO_CONTEXT
+                    || operation.getContexts().length is 1) {
+                // remove the operation if it only has the context or we are
+                // flushing all
+                synchronized(redoList) redoList.remove(operation);
+                internalRemove(operation);
+            } else {
+                // remove the reference to the context.
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
+                // It is not enough to simply remove the context. There could
+                // be one or more contexts that match the one we are trying to
+                // dispose.
+                IUndoContext[] contexts = operation.getContexts();
+                for (int j = 0; j < contexts.length; j++) {
+                    if (contexts[j].matches(context)) {
+                        operation.removeContext(contexts[j]);
+                    }
+                }
+                if (operation.getContexts().length is 0) {
+                    synchronized(redoList) redoList.remove(operation);
+                    internalRemove(operation);
+                }
+            }
+        }
+    }
+
+    /*
+     * Flush the undo stack of all operations that have the given context.
+     */
+    private void flushUndo(IUndoContext context) {
+        if (DEBUG_OPERATION_HISTORY_DISPOSE) {
+            Tracing.printTrace("OPERATIONHISTORY", "Flushing undo history for " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)context).toString );
+        }
+
+        // Get all operations that have the context (or one that matches)
+        IUndoableOperation[] filtered = filter(undoList, context);
+        for (int i = 0; i < filtered.length; i++) {
+            IUndoableOperation operation = filtered[i];
+            if (context is GLOBAL_UNDO_CONTEXT
+                    || operation.getContexts().length is 1) {
+                // remove the operation if it only has the context or we are
+                // flushing all
+                synchronized(undoList) undoList.remove(operation);
+                internalRemove(operation);
+            } else {
+                // remove the reference to the context.
+                // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
+                // It is not enough to simply remove the context. There could
+                // be one or more contexts that match the one we are trying to
+                // dispose.
+                IUndoContext[] contexts = operation.getContexts();
+                for (int j = 0; j < contexts.length; j++) {
+                    if (contexts[j].matches(context)) {
+                        operation.removeContext(contexts[j]);
+                    }
+                }
+                if (operation.getContexts().length is 0) {
+                    synchronized(undoList) undoList.remove(operation);
+                    internalRemove(operation);
+                }
+            }
+        }
+        /*
+         * There may be an open composite. If it has this context, then the
+         * context must be removed. If it has only this context or we are
+         * flushing all operations, then null it out and notify that we are
+         * ending it. We don't remove it since it was never added.
+         */
+        ICompositeOperation endedComposite = null;
+        synchronized (openCompositeLock) {
+            if (openComposite !is null) {
+                if (openComposite.hasContext(context)) {
+                    if (context is GLOBAL_UNDO_CONTEXT
+                            || openComposite.getContexts().length is 1) {
+                        endedComposite = openComposite;
+                        openComposite = null;
+                    } else {
+                        openComposite.removeContext(context);
+                    }
+                }
+            }
+        }
+        // notify outside of the synchronized block.
+        if (endedComposite !is null) {
+            notifyNotOK(endedComposite);
+        }
+    }
+
+    /*
+     * Force the redo history for the given context to contain max or less
+     * items.
+     */
+    private void forceRedoLimit(IUndoContext context, int max) {
+        IUndoableOperation[] filtered = filter(redoList, context);
+        int size = filtered.length;
+        if (size > 0) {
+            int index = 0;
+            while (size > max) {
+                IUndoableOperation removed = filtered[index];
+                if (context is GLOBAL_UNDO_CONTEXT
+                        || removed.getContexts().length is 1) {
+                    /*
+                     * remove the operation if we are enforcing a global limit
+                     * or if the operation only has the specified context
+                     */
+                    synchronized(redoList) redoList.remove(removed);
+                    internalRemove(removed);
+                } else {
+                    /*
+                     * if the operation has multiple contexts and we've reached
+                     * the limit for only one of them, then just remove the
+                     * context, not the operation.
+                     */
+                    removed.removeContext(context);
+                }
+                size--;
+                index++;
+            }
+        }
+    }
+
+    /*
+     * Force the undo history for the given context to contain max or less
+     * items.
+     */
+    private void forceUndoLimit(IUndoContext context, int max) {
+        IUndoableOperation[] filtered = filter(undoList, context);
+        int size = filtered.length;
+        if (size > 0) {
+            int index = 0;
+            while (size > max) {
+                IUndoableOperation removed = filtered[index];
+                if (context is GLOBAL_UNDO_CONTEXT
+                        || removed.getContexts().length is 1) {
+                    /*
+                     * remove the operation if we are enforcing a global limit
+                     * or if the operation only has the specified context
+                     */
+                    synchronized(undoList) undoList.remove(removed);
+                    internalRemove(removed);
+                } else {
+                    /*
+                     * if the operation has multiple contexts and we've reached
+                     * the limit for only one of them, then just remove the
+                     * context, not the operation.
+                     */
+                    removed.removeContext(context);
+                }
+                size--;
+                index++;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#getLimit()
+     */
+    public int getLimit(IUndoContext context) {
+        synchronized(limits) {
+            if (!limits.containsKey(context)) {
+                return DEFAULT_LIMIT;
+            }
+            return (cast(Integer) (limits.get(context))).intValue();
+        }
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed redo should be
+     * allowed.
+     */
+    private IStatus getRedoApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            IOperationApprover approver = cast(IOperationApprover) approverArray[i];
+            IStatus approval = approver.proceedRedoing(operation, this, info);
+            if (!approval.isOK()) {
+                if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Redo not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                    ~ "for operation " ~ (cast(Object)operation).toString //$NON-NLS-1$
+                                    ~ " approved by " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                }
+                return approval;
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#getRedoHistory(dwtx.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation[] getRedoHistory(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        return filter(redoList, context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#getOperation(dwtx.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation getRedoOperation(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        synchronized (undoRedoHistoryLock) {
+            for (int i = redoList.size() - 1; i >= 0; i--) {
+                IUndoableOperation operation;
+                synchronized(redoList) operation = cast(IUndoableOperation) redoList.get(i);
+                if (operation.hasContext(context)) {
+                    return operation;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed undo should be
+     * allowed.
+     */
+    private IStatus getUndoApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        final Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            IOperationApprover approver = cast(IOperationApprover) approverArray[i];
+            IStatus approval = approver.proceedUndoing(operation, this, info);
+            if (!approval.isOK()) {
+                if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Undo not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                    ~ "for operation " ~ (cast(Object)operation ).toString//$NON-NLS-1$
+                                    ~ " with status " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                }
+                return approval;
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#getUndoHistory(dwtx.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation[] getUndoHistory(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        return filter(undoList, context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#getUndoOperation(dwtx.core.commands.operations.IUndoContext)
+     */
+    public IUndoableOperation getUndoOperation(IUndoContext context) {
+        Assert.isNotNull(cast(Object)context);
+        synchronized (undoRedoHistoryLock) {
+            for (int i = undoList.size() - 1; i >= 0; i--) {
+                IUndoableOperation operation;
+                synchronized(undoList) operation = cast(IUndoableOperation) undoList.get(i);
+                if (operation.hasContext(context)) {
+                    return operation;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Consult the IOperationApprovers to see if the proposed execution should
+     * be allowed.
+     *
+     * @since 3.2
+     */
+    private IStatus getExecuteApproval(IUndoableOperation operation,
+            IAdaptable info) {
+
+        final Object[] approverArray = approvers.getListeners();
+
+        for (int i = 0; i < approverArray.length; i++) {
+            if ( auto apro = cast(IOperationApprover2)approverArray[i]  ) {
+                IOperationApprover2 approver = apro;
+                IStatus approval = approver.proceedExecuting(operation, this,
+                        info);
+                if (!approval.isOK()) {
+                    if (DEBUG_OPERATION_HISTORY_APPROVAL) {
+                        Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                                "Execute not approved by " ~ (cast(Object)approver).toString //$NON-NLS-1$
+                                        ~ "for operation " ~ (cast(Object)operation).toString //$NON-NLS-1$
+                                        ~ " with status " ~ (cast(Object)approval).toString); //$NON-NLS-1$
+                    }
+                    return approval;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * Remove the operation by disposing it and notifying listeners.
+     */
+    private void internalRemove(IUndoableOperation operation) {
+        operation.dispose();
+        notifyRemoved(operation);
+    }
+
+    /*
+     * Notify listeners of an operation event.
+     */
+    private void notifyListeners(OperationHistoryEvent event) {
+        if ( auto e = cast(IAdvancedUndoableOperation)event.getOperation()  ) {
+            IAdvancedUndoableOperation advancedOp = e;
+            SafeRunner.run(new class() ISafeRunnable {
+                IAdvancedUndoableOperation advancedOp_;
+                this(){ advancedOp_=advancedOp;}
+                public void handleException(Exception exception) {
+                    if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                        Tracing.printTrace(
+                                        "OPERATIONHISTORY", //$NON-NLS-1$
+                                        "Exception during notification callback " ~ exception.toString); //$NON-NLS-1$
+                    }
+                }
+
+                public void run() {
+                    advancedOp_.aboutToNotify(event);
+                }
+            });
+        }
+        Object[] listenerArray = listeners.getListeners();
+        for (int i = 0; i < listenerArray.length; i++) {
+            IOperationHistoryListener listener = cast(IOperationHistoryListener) listenerArray[i];
+            SafeRunner.run(new class() ISafeRunnable {
+                IOperationHistoryListener listener_;
+                this(){ listener_=listener; }
+                public void handleException(Exception exception) {
+                    if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                        Tracing.printTrace(
+                                        "OPERATIONHISTORY", //$NON-NLS-1$
+                                        "Exception during notification callback " ~ exception.toString); //$NON-NLS-1$
+                    }
+                }
+
+                public void run() {
+                    listener_.historyNotification(event);
+                }
+            });
+        }
+    }
+
+    private void notifyAboutToExecute(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_EXECUTE " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_EXECUTE, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is about to redo.
+     */
+    private void notifyAboutToRedo(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_REDO " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_REDO, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is about to undo.
+     */
+    private void notifyAboutToUndo(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_UNDO " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.ABOUT_TO_UNDO, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been added.
+     */
+    private void notifyAdd(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_ADDED " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_ADDED, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation is done executing.
+     */
+    private void notifyDone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "DONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.DONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation did not succeed after an attempt to
+     * execute, undo, or redo was made.
+     */
+    private void notifyNotOK(IUndoableOperation operation) {
+        notifyNotOK(operation, null);
+    }
+
+    /*
+     * Notify listeners that an operation did not succeed after an attempt to
+     * execute, undo, or redo was made. Include the status associated with the
+     * attempt.
+     *
+     * @since 3.2
+     */
+    private void notifyNotOK(IUndoableOperation operation, IStatus status) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_NOT_OK " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_NOT_OK, this, operation, status));
+    }
+
+    /*
+     * Notify listeners that an operation was redone.
+     */
+    private void notifyRedone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "REDONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.REDONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been removed from the history.
+     */
+    private void notifyRemoved(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_REMOVED " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_REMOVED, this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been undone.
+     */
+    private void notifyUndone(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "UNDONE " ~ (cast(Object)operation).toString); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.UNDONE,
+                this, operation));
+    }
+
+    /*
+     * Notify listeners that an operation has been undone.
+     */
+    private void notifyChanged(IUndoableOperation operation) {
+        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_CHANGED " //$NON-NLS-1$//$NON-NLS-2$
+                    ~ (cast(Object)operation).toString);
+        }
+
+        notifyListeners(new OperationHistoryEvent(
+                OperationHistoryEvent.OPERATION_CHANGED, this, operation));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#redo(dwtx.core.commands.operations.IUndoContext,
+     *      dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus redo(IUndoContext context, IProgressMonitor monitor,
+            IAdaptable info) {
+        Assert.isNotNull(cast(Object)context);
+        IUndoableOperation operation = getRedoOperation(context);
+
+        // info if there is no operation
+        if (operation is null) {
+            return IOperationHistory.NOTHING_TO_REDO_STATUS;
+        }
+
+        // error if operation is invalid
+        if (!operation.canRedo()) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Redo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        return doRedo(monitor, info, operation);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#redoOperation(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+
+    public IStatus redoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+        IStatus status;
+        if (operation.canRedo()) {
+            status = doRedo(monitor, info, operation);
+        } else {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Redo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            status = IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#removeOperationApprover(dwtx.core.commands.operations.IOperationApprover)
+     */
+    public void removeOperationApprover(IOperationApprover approver) {
+        approvers.remove(cast(Object)approver);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#removeOperationHistoryListener(dwtx.core.commands.operations.IOperationHistoryListener)
+     */
+    public void removeOperationHistoryListener(
+            IOperationHistoryListener listener) {
+        listeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#replaceOperation(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.commands.operations.IUndoableOperation [])
+     */
+    public void replaceOperation(IUndoableOperation operation,
+            IUndoableOperation[] replacements) {
+        // check the undo history first.
+        bool inUndo = false;
+        synchronized (undoRedoHistoryLock) {
+            int index = 0;
+            synchronized(undoList) {
+                foreach( o; undoList ){
+                    if( o == operation ){
+                        break;
+                    }
+                    index++;
+                }
+                if( index is undoList.size() ){
+                    index = -1;
+                }
+            }
+            if (index > -1) {
+                inUndo = true;
+                synchronized(undoList) undoList.remove(operation);
+                // notify listeners after the lock on undoList is released
+                auto allContexts = new ArraySeq!(IUndoContext);
+                allContexts.capacity(replacements.length);
+                for (int i = 0; i < replacements.length; i++) {
+                    IUndoContext[] opContexts = replacements[i].getContexts();
+                    for (int j = 0; j < opContexts.length; j++) {
+                        allContexts.append(opContexts[j]);
+                    }
+                    synchronized(undoList) undoList.addAt(index, replacements[i]);
+                    // notify listeners after the lock on the history is
+                    // released
+                }
+                // recheck all the limits. We do this at the end so the index
+                // doesn't change during replacement
+                for (int i = 0; i < allContexts.size(); i++) {
+                    IUndoContext context = allContexts.get(i);
+                    forceUndoLimit(context, getLimit(context));
+                }
+            }
+        }
+        if (inUndo) {
+            // notify listeners of operations added and removed
+            internalRemove(operation);
+            for (int i = 0; i < replacements.length; i++) {
+                notifyAdd(replacements[i]);
+            }
+            return;
+        }
+
+        // operation was not in the undo history. Check the redo history.
+
+        synchronized (undoRedoHistoryLock) {
+            int index = 0;
+            synchronized(redoList) {
+                foreach( o; redoList ){
+                    if( o == operation ){
+                        break;
+                    }
+                    index++;
+                }
+                if( index is redoList.size() ){
+                    index = -1;
+                }
+            }
+            if (index is -1) {
+                return;
+            }
+            auto allContexts = new ArraySeq!(IUndoContext);
+            allContexts.capacity(replacements.length);
+            synchronized(redoList) redoList.remove(operation);
+            // notify listeners after we release the lock on redoList
+            for (int i = 0; i < replacements.length; i++) {
+                IUndoContext[] opContexts = replacements[i].getContexts();
+                for (int j = 0; j < opContexts.length; j++) {
+                    allContexts.append(opContexts[j]);
+                }
+                synchronized(redoList) redoList.addAt(index, replacements[i]);
+                // notify listeners after we release the lock on redoList
+            }
+            // recheck all the limits. We do this at the end so the index
+            // doesn't change during replacement
+            for (int i = 0; i < allContexts.size(); i++) {
+                IUndoContext context = cast(IUndoContext) allContexts.get(i);
+                forceRedoLimit(context, getLimit(context));
+            }
+        }
+        // send listener notifications after we release the lock on the history
+        internalRemove(operation);
+        for (int i = 0; i < replacements.length; i++) {
+            notifyAdd(replacements[i]);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#setLimit(dwtx.core.commands.operations.IUndoContext,
+     *      int)
+     */
+    public void setLimit(IUndoContext context, int limit) {
+        Assert.isTrue(limit >= 0);
+        /*
+         * The limit checking methods interpret a null context as a global limit
+         * to be enforced. We do not wish to support a global limit in this
+         * implementation, so we throw an exception for a null context. The rest
+         * of the implementation can handle a null context, so subclasses can
+         * override this if a global limit is desired.
+         */
+        Assert.isNotNull(cast(Object)context);
+        synchronized(limits) limits.add(context, new Integer(limit));
+        forceUndoLimit(context, limit);
+        forceRedoLimit(context, limit);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#undo(dwtx.core.commands.operations.IUndoContext,
+     *      dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus undo(IUndoContext context, IProgressMonitor monitor,
+            IAdaptable info) {
+        Assert.isNotNull(cast(Object)context);
+        IUndoableOperation operation = getUndoOperation(context);
+
+        // info if there is no operation
+        if (operation is null) {
+            return IOperationHistory.NOTHING_TO_UNDO_STATUS;
+        }
+
+        // error if operation is invalid
+        if (!operation.canUndo()) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Undo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            return IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+
+        return doUndo(monitor, info, operation);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#undoOperation(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus undoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info) {
+        Assert.isNotNull(cast(Object)operation);
+        IStatus status;
+        if (operation.canUndo()) {
+            status = doUndo(monitor, info, operation);
+        } else {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                        "Undo operation not valid - " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+            }
+            status = IOperationHistory.OPERATION_INVALID_STATUS;
+        }
+        return status;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#openOperation(dwtx.core.commands.operations.ICompositeOperation)
+     */
+    public void openOperation(ICompositeOperation operation, int mode) {
+        synchronized (openCompositeLock) {
+            if (openComposite !is null && openComposite !is operation) {
+                // unexpected nesting of operations.
+                if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Open operation called while another operation is open.  old: " //$NON-NLS-1$
+                                    ~ (cast(Object)openComposite).toString ~ "; new:  " ~ (cast(Object)operation).toString); //$NON-NLS-1$
+                }
+
+                throw new IllegalStateException(
+                        "Cannot open an operation while one is already open"); //$NON-NLS-1$
+            }
+            openComposite = operation;
+        }
+        if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
+            Tracing.printTrace("OPERATIONHISTORY", "Opening operation " //$NON-NLS-1$ //$NON-NLS-2$
+                    ~ (cast(Object)openComposite).toString);
+        }
+
+        if (mode is EXECUTE) {
+            notifyAboutToExecute(openComposite);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#closeOperation(bool,
+     *      bool)
+     */
+    public void closeOperation(bool operationOK, bool addToHistory,
+            int mode) {
+        ICompositeOperation endedComposite = null;
+
+        synchronized (openCompositeLock) {
+            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
+                if (openComposite is null) {
+                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
+                            "Attempted to close operation when none was open"); //$NON-NLS-1$
+                    return;
+                }
+            }
+            // notifications will occur outside the synchonized block
+            if (openComposite !is null) {
+                if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
+                    Tracing.printTrace("OPERATIONHISTORY", "Closing operation " //$NON-NLS-1$ //$NON-NLS-2$
+                            ~ (cast(Object)openComposite).toString);
+                }
+                endedComposite = openComposite;
+                openComposite = null;
+            }
+        }
+        // any mode other than EXECUTE was triggered by a request to undo or
+        // redo something already in the history, so undo and redo
+        // notification will occur at the end of that sequence.
+        if (endedComposite !is null) {
+            if (operationOK) {
+                if (mode is EXECUTE) {
+                    notifyDone(endedComposite);
+                }
+                if (addToHistory) {
+                    add(endedComposite);
+                }
+            } else {
+                if (mode is EXECUTE) {
+                    notifyNotOK(endedComposite);
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationHistory#operationChanged(dwtx.core.commands.operations.IUndoableOperation)
+     */
+    public void operationChanged(IUndoableOperation operation) {
+        if (undoList.contains(operation) || redoList.contains(operation)) {
+            notifyChanged(operation);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IAdvancedUndoableOperation.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.IAdvancedUndoableOperation;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.OperationHistoryEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * IAdvancedUndoableOperation defines an interface for undoable operations that
+ * modify one or more elements in a model and attempt to keep model listeners up
+ * to date with changes that occur in the undo and redo history involving particular
+ * model elements.  It also defines methods for computing the validity of an operation
+ * for undo or redo before attempting to perform the undo or redo.
+ * </p>
+ * <p>
+ * This interface is intended to be used by legacy frameworks that are adapting
+ * their original undo and redo support to this framework. The methods in this
+ * interface allow legacy clients to maintain features not supported in the
+ * basic operations framework.
+ * </p>
+ *
+ * @since 3.1
+ *
+ */
+public interface IAdvancedUndoableOperation {
+
+    /**
+     * <p>
+     * An operation history notification about this operation is about to be
+     * sent to operation history listeners. Any preparation needed before
+     * listeners are notified about this operation should be performed here.
+     *
+     * <p>
+     * This method has been added to support legacy undo frameworks that are
+     * adapting to IUndoableOperation. Operations that previously relied on
+     * notification from their containing history or stack before any listeners
+     * are notified about changes to the operation should implement this
+     * interface.
+     *
+     * @param event
+     *            the event that is about to be sent with the pending
+     *            notification
+     *
+     */
+    void aboutToNotify(OperationHistoryEvent event);
+
+    /**
+     * <p>
+     * Return an array of objects that are affected by executing, undoing, or
+     * redoing this operation. If it cannot be determined which objects are
+     * affected, return null.
+     * </p>
+     *
+     * @return the array of Objects modified by this operation, or
+     *         <code>null</code> if the affected objects cannot be determined.
+     */
+    Object[] getAffectedObjects();
+
+    /**
+     * Return a status indicating the projected outcome of undoing the receiver.
+     *
+     * This method should be used to report the possible outcome of an undo and
+     * is used when computing the validity of an undo is too expensive to
+     * perform in {@link IUndoableOperation#canUndo()}. It is not called by the
+     * operation history, but instead is used by clients (such as implementers
+     * of {@link IOperationApprover}) who wish to perform advanced validation of
+     * an operation before attempting to undo it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be undone, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canUndo()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the undo. The status
+     *         severity should be set to <code>OK</code> if the undo can
+     *         successfully be performed, and <code>ERROR</code> if it
+     *         cannnot. Any other status is assumed to represent an ambiguous
+     *         state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeUndoableStatus(IProgressMonitor monitor);
+
+    /**
+     * Return a status indicating the projected outcome of redoing the receiver.
+     *
+     * This method should be used to report the possible outcome of a redo and
+     * is used when computing the validity of a redo is too expensive to perform
+     * in {@link IUndoableOperation#canRedo()}. It is not called by the
+     * operation history, but instead is used by clients (such as implementers
+     * of {@link IOperationApprover}) who wish to perform advanced validation of
+     * an operation before attempting to redo it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be redone, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canRedo()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the redo. The status
+     *         severity should be set to <code>OK</code> if the redo can
+     *         successfully be performed, and <code>ERROR</code> if it
+     *         cannnot. Any other status is assumed to represent an ambiguous
+     *         state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeRedoableStatus(IProgressMonitor monitor);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IAdvancedUndoableOperation2.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.IAdvancedUndoableOperation2;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * IAdvancedUndoableOperation2 defines a method for computing the validity of
+ * executing an operation before attempting to execute it. It also defines a way
+ * for clients to specify that computing status should be done quietly, without
+ * consulting the user. This interface is useful when implementing
+ * {@link IOperationApprover2}, or any other object that performs validation of
+ * the undo history. It also allows operations to specify whether they should be
+ * run in the UI thread.
+ * </p>
+ *
+ * @since 3.3
+ *
+ */
+public interface IAdvancedUndoableOperation2 {
+    /**
+     * Return a status indicating the projected outcome of executing the
+     * receiver.
+     *
+     * This method should be used to report the possible outcome of executing an
+     * operation when computing the validity of an execute is too expensive to
+     * perform in {@link IUndoableOperation#canExecute()}. It is not called by
+     * the operation history, but instead is used by clients (such as
+     * implementers of {@link IOperationApprover2}) who wish to perform
+     * advanced validation of an operation before attempting to execute it.
+     *
+     * If the result of this method is the discovery that an operation can in
+     * fact not be executed, then the operation is expected to correctly answer
+     * <code>false</code> on subsequent calls to
+     * {@link IUndoableOperation#canExecute()}.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user while computing the validity.
+     *
+     * @return the IStatus indicating the validity of the execute. The status
+     *         severity should be set to <code>OK</code> if the execute can
+     *         successfully be performed, and <code>ERROR</code> if it cannot.
+     *         Any other severity is assumed to represent an ambiguous state.
+     * @throws ExecutionException
+     *             if an exception occurs while computing the validity.
+     */
+    IStatus computeExecutionStatus(IProgressMonitor monitor);
+
+    /**
+     * Set a bool that instructs whether the computation of the receiver's
+     * execution, undo, or redo status should quietly compute status without
+     * consulting or prompting the user. The default value is <code>false</code>.
+     * This flag should only be set to <code>true</code> while the execution,
+     * undo, or redo status computations are being performed in the background,
+     * and should be restored to <code>false</code> when complete.
+     * <p>
+     * If the status computation methods typically need to consult the user in
+     * order to determine the severity of a particular situation, the least
+     * severe status that could be chosen by the user should be returned when
+     * this flag is <code>true</code>. This can help to prevent overzealous
+     * disposal of the operation history when an operation is in an ambiguous
+     * state. Typically, the status computation methods are invoked with this
+     * flag set to <code>false</code> just before the actual execution, undo,
+     * or redo occurs, so the user can be consulted for the final outcome.
+     *
+     * @param quiet
+     *            <code>true</code> if it is inappropriate to consult or
+     *            otherwise prompt the user while computing status, and
+     *            <code>false</code> if the user may be prompted.
+     *
+     * @see #computeExecutionStatus(IProgressMonitor)
+     * @see IAdvancedUndoableOperation#computeUndoableStatus(IProgressMonitor)
+     * @see IAdvancedUndoableOperation#computeRedoableStatus(IProgressMonitor)
+     */
+    public void setQuietCompute(bool quiet);
+
+    /**
+     * Return a bool that instructs whether the operation should be executed,
+     * undone, or redone in a background thread.
+     *
+     * @return <code>true</code> if the operation should be run in the
+     *         background, <code>false</code> if it should not.
+     */
+    public bool runInBackground();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/ICompositeOperation.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.ICompositeOperation;
+
+import dwtx.core.commands.operations.IUndoableOperation;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * ICompositeOperation defines an undoable operation that is composed of child
+ * operations. Requests to execute, undo, or redo a composite result in the the
+ * execution, undo, or redo of the composite as a whole. Similarly, a request to
+ * dispose the composite should result in all child operations being disposed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface ICompositeOperation : IUndoableOperation {
+
+    /**
+     * <p>
+     * Add the specified operation as a child of this operation.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be added. If the operation instance has
+     *            already been added, this method will have no effect.
+     */
+    void add(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Remove the specified operation from this operation.
+     * </p>
+     * <p>
+     * The composite operation should dispose the operation as part of removing
+     * it.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be removed. The operation should be disposed
+     *            by the receiver. This method will have no effect if the
+     *            operation instance is not already a child.
+     */
+    void remove(IUndoableOperation operation);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IContextReplacingOperation.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.IContextReplacingOperation;
+
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * IContextReplacingOperation defines an interface for undoable operations that
+ * can replace one undo context with another undo context. It is used by
+ * operations, such as composite operations, where removing and adding an undo
+ * context would not have the same semantic as replacing one undo context with
+ * another.
+ *
+ * @since 3.2
+ *
+ */
+public interface IContextReplacingOperation {
+
+    /**
+     * Replace the undo context of the receiver with the provided replacement
+     * undo context.
+     * <p>
+     * This message has no effect if the original undo context is not present in
+     * the receiver.
+     *
+     * @param original the undo context which is to be replaced
+     * @param replacement the undo context which is replacing the original
+     *
+     */
+    void replaceContext(IUndoContext original, IUndoContext replacement);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IOperationApprover.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.IOperationApprover;
+
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IOperationHistory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * IOperationApprover defines an interface for approving the undo or redo of a
+ * particular operation within an operation history. Operations that are
+ * candidates for undo or redo have already been validated against their current
+ * state and according to the rules of the history.
+ * </p>
+ * <p>
+ * By the time an IOperationApprover is consulted, the undo has already been
+ * requested. Approvers should return an <code>IStatus</code> object with
+ * severity <code>OK</code> if the operation should proceed, and any other
+ * severity if it should not. When an operation is not approved, it is expected
+ * that the object not allowing the operation has already consulted the user if
+ * necessary or otherwise provided any necessary information to the user about
+ * the fact that the operation is not approved.
+ * </p>
+ * <p>
+ * Operation approvers must be prepared to receive the approval messages from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationApprover {
+
+    /**
+     * Return a status indicating whether the specified operation should be
+     * redone. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the redo will
+     * be performed when the status is <code>OK</code>, since other operation
+     * approvers may veto the redo.
+     *
+     * @param operation
+     *            the operation to be redone
+     * @param history
+     *            the history redoing the operation
+     * @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 dwt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         redo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the redo will be
+     *         returned the status that caused the rejection. Any other status
+     *         severities will not be interpreted by the history.
+     */
+    IStatus proceedRedoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+
+    /**
+     * Return a status indicating whether the specified operation should be
+     * undone. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the undo will
+     * be performed when the status is <code>OK</code>, since other operation
+     * approvers can veto the undo.
+     *
+     * @param operation
+     *            the operation to be undone
+     * @param history
+     *            the history undoing the operation
+     * @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 dwt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         undo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the undo will be
+     *         returned the status that caused the rejection. Any other status
+     *         severities will not be interpreted by the history.
+     */
+    IStatus proceedUndoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IOperationApprover2.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.IOperationApprover2;
+
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.IOperationApprover;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IOperationHistory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Extends {@link IOperationApprover} to approve the execution of a particular
+ * operation within an operation history. Operations that are candidates for
+ * execution have already been validated against their current state and
+ * according to the rules of the history. Prior to 3.2, an operation approver
+ * was only consulted for undo and redo of an operation, not its initial
+ * execution.
+ * <p>
+ * By the time an IOperationApprover2 is consulted, the execution has already
+ * been requested and it has been determined that the operation is valid.
+ * Approvers should return an <code>IStatus</code> object with severity
+ * <code>OK</code> if the operation should proceed, and any other severity if
+ * it should not. When an operation is not approved, it is expected that the
+ * object not allowing the operation has already consulted the user if necessary
+ * or otherwise provided any necessary information to the user about the fact
+ * that the operation is not approved.
+ * </p>
+ * <p>
+ * Like {@link IOperationApprover}, implementers of this extension must be
+ * prepared to receive the approval messages from a background thread. Any UI
+ * access occurring inside the implementation must be properly synchronized
+ * using the techniques specified by the client's widget library.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IOperationApprover2 : IOperationApprover {
+    /**
+     * Return a status indicating whether the specified operation should be
+     * executed. Any status that does not have severity <code>IStatus.OK</code>
+     * will not be approved. Implementers should not assume that the execution
+     * will be performed when the status is <code>OK</code>, since other
+     * operation approvers may veto the execution.
+     *
+     * @param operation
+     *            the operation to be executed
+     * @param history
+     *            the history performing the execution of the operation
+     * @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 dwt.widgets.Shell.class. Even if UI
+     *            information is provided, the implementation of this method
+     *            must be prepared for being called from a background thread.
+     *            Any UI access must be properly synchronized using the
+     *            techniques specified by the client's widget library.
+     * @return the IStatus describing whether the operation is approved. The
+     *         execution will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the execution will
+     *         be returned the status that caused the rejection. Any other
+     *         status severities will not be interpreted by the history.
+     */
+    IStatus proceedExecuting(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IOperationHistory.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,712 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.IOperationHistory;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.internal.commands.operations.GlobalUndoContext;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.IUndoContext;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IOperationApprover;
+import dwtx.core.commands.operations.IOperationApprover2;
+import dwtx.core.commands.operations.IOperationHistoryListener;
+import dwtx.core.commands.operations.ICompositeOperation;
+import dwtx.core.commands.operations.OperationStatus;
+
+import dwt.dwthelper.utils;
+
+static this(){
+    IOperationHistory.GLOBAL_UNDO_CONTEXT = new GlobalUndoContext();
+    IOperationHistory.NOTHING_TO_REDO_STATUS = new OperationStatus(
+        IStatus.INFO, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.NOTHING_TO_REDO, "No operation to redo", null); //$NON-NLS-1$
+    IOperationHistory.NOTHING_TO_UNDO_STATUS = new OperationStatus(
+        IStatus.INFO, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.NOTHING_TO_UNDO, "No operation to undo", null); //$NON-NLS-1$
+    IOperationHistory.OPERATION_INVALID_STATUS = new OperationStatus(
+        IStatus.ERROR, OperationStatus.DEFAULT_PLUGIN_ID,
+        OperationStatus.OPERATION_INVALID, "Operation is not valid", null); //$NON-NLS-1$
+}
+
+/**
+ * <p>
+ * IOperationHistory tracks a history of operations that can be undone or
+ * redone. Operations are added to the history once they have been initially
+ * executed. Clients may choose whether to have the operations history perform
+ * the initial execution or to simply add an already-executed operation to the
+ * history.
+ * </p>
+ * <p>
+ * Once operations are added to the history, the methods
+ * {@link #canRedo(IUndoContext)} and {@link #canUndo(IUndoContext)} are used to
+ * determine whether there is an operation available for undo and redo in a
+ * given undo context. The context-based protocol implies that there is only one
+ * operation that can be undone or redone at a given time in a given context.
+ * This is typical of a linear undo model, when only the most recently executed
+ * operation is available for undo. When this protocol is used, a linear model
+ * is enforced by the history.
+ * </p>
+ * <p>
+ * It is up to clients to determine how to maintain a history that is invalid or
+ * stale. For example, when the most recent operation for a context cannot be
+ * performed, clients may wish to dispose the history for that context.
+ * </p>
+ * <p>
+ * Additional protocol allows direct undo and redo of a specified operation,
+ * regardless of its position in the history. When a more flexible undo model is
+ * supported, these methods can be implemented to undo and redo directly
+ * specified operations. If an implementer of IOperationHistory does not allow
+ * direct undo and redo, these methods can return a status indicating that it is
+ * not allowed.
+ * </p>
+ * <p>
+ * Listeners ({@link IOperationHistoryListener}) can listen for notifications
+ * about changes in the history (operations added or removed), and for
+ * notification before and after any operation is executed, undone or redone.
+ * Notification of operation execution only occurs when clients direct the
+ * history to execute the operation. If the operation is added after it is
+ * executed, there can be no notification of its execution.
+ * </p>
+ * <p>
+ * {@link IOperationApprover} defines an interface for approving an undo or redo
+ * before it occurs. This is useful for injecting policy-decisions into the undo
+ * model - whether direct undo and redo are supported, or warning the user about
+ * certain kinds of operations. It can also be used when clients maintain state
+ * related to an operation and need to determine whether an undo or redo will
+ * cause any conflicts with their local state.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationHistory {
+
+    /**
+     * An operation is to be opened or closed for execution. (value is 1).
+     */
+    public static const int EXECUTE = 1;
+
+    /**
+     * An operation is to be opened for undo. (value is 2).
+     */
+    public static const int UNDO = 2;
+
+    /**
+     * An operation is to be opened for redo. (value is 3).
+     */
+    public static const int REDO = 3;
+
+    /**
+     * An undo context that can be used to refer to the global undo history.
+     * This context is not intended to be assigned to operations. Instead, it is
+     * used for querying the history or performing an undo or redo on the entire
+     * history, regardless of each operation's undo contexts.
+     */
+    public static /+const+/ IUndoContext GLOBAL_UNDO_CONTEXT;
+
+    /**
+     * An operation info status describing the condition that there is no
+     * available operation for redo.
+     */
+    public static /+const+/ IStatus NOTHING_TO_REDO_STATUS; //$NON-NLS-1$
+
+    /**
+     * An operation info status describing the condition that there is no
+     * available operation for undo.
+     */
+    public static /+const+/ IStatus NOTHING_TO_UNDO_STATUS; //$NON-NLS-1$
+
+    /**
+     * An operation error status describing the condition that the operation
+     * available for execution, undo or redo is not in a valid state for the
+     * action to be performed.
+     */
+    public static /+const+/ IStatus OPERATION_INVALID_STATUS; //$NON-NLS-1$
+
+    /**
+     * <p>
+     * Add the specified operation to the history without executing it. The
+     * operation should have already been executed by the time it is added to
+     * the history. Listeners will be notified that the operation was added to
+     * the history (<code>OPERATION_ADDED</code>).
+     * </p>
+     *
+     * @param operation
+     *            the operation to be added to the history
+     */
+    void add(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Add the specified approver to the list of operation approvers consulted
+     * by the operation history before an undo or redo is attempted.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be added as an approver.the instance
+     *            to remove. Must not be <code>null</code>. If an attempt is
+     *            made to register an instance which is already registered with
+     *            this instance, this method has no effect.
+     *
+     * @see dwtx.core.commands.operations.IOperationApprover
+     */
+    void addOperationApprover(IOperationApprover approver);
+
+    /**
+     * <p>
+     * Add the specified listener to the list of operation history listeners
+     * that are notified about changes in the history or operations that are
+     * executed, undone, or redone.
+     * </p>
+     *
+     * @param listener
+     *            the IOperationHistoryListener to be added as a listener. Must
+     *            not be <code>null</code>. If an attempt is made to register
+     *            an instance which is already registered with this instance,
+     *            this method has no effect.
+     *
+     * @see dwtx.core.commands.operations.IOperationHistoryListener
+     * @see dwtx.core.commands.operations.OperationHistoryEvent
+     */
+    void addOperationHistoryListener(IOperationHistoryListener listener);
+
+    /**
+     * <p>
+     * Close the current operation. If the operation has successfully completed,
+     * send listeners a <code>DONE</code>, <code>UNDONE</code>, or
+     * <code>REDONE</code> notification, depending on the mode. Otherwise send
+     * an <code>OPERATION_NOT_OK</code> notification. Add the operation to the
+     * history if specified and send an <code>OPERATION_ADDED</code>
+     * notification.
+     * </p>
+     * <p>
+     * Any operations that are executed and added after this operation is closed
+     * will no longer be considered part of this operation.
+     * </p>
+     * <p>
+     * This method has no effect if the caller has not previously called
+     * {@link #openOperation}.
+     * </p>
+     *
+     * @param operationOK
+     *            <code>true</code> if the operation successfully completed.
+     *            Listeners should be notified with <code>DONE</code>,
+     *            <code>UNDONE</code>, or <code>REDONE</code>.
+     *            <code>false</code> if the operation did not successfully
+     *            complete. Listeners should be notified with
+     *            <code>OPERATION_NOT_OK</code>.
+     * @param addToHistory
+     *            <code>true</code> if the operation should be added to the
+     *            history, <code>false</code> if it should not. If the
+     *            <code>operationOK</code> parameter is <code>false</code>,
+     *            the operation will never be added to the history.
+     * @param mode
+     *            the mode the operation was opened in. Can be one of
+     *            <code>EXECUTE</code>, <code>UNDO</code>, or
+     *            <code>REDO</code>. This determines what notifications are
+     *            sent.
+     */
+    void closeOperation(bool operationOK, bool addToHistory, int mode);
+
+    /**
+     * <p>
+     * Return whether there is a valid redoable operation available in the given
+     * context.
+     * </p>
+     *
+     * @param context
+     *            the context to be checked
+     * @return <code>true</code> if there is a redoable operation,
+     *         <code>false</code> otherwise.
+     */
+
+    bool canRedo(IUndoContext context);
+
+    /**
+     * <p>
+     * Return whether there is a valid undoable operation available in the given
+     * context
+     * </p>
+     *
+     * @param context
+     *            the context to be checked
+     * @return <code>true</code> if there is an undoable operation,
+     *         <code>false</code> otherwise.
+     */
+    bool canUndo(IUndoContext context);
+
+    /**
+     * <p>
+     * Dispose of the specified context in the history. All operations that have
+     * only the given context will be disposed. References to the context in
+     * operations that have more than one context will also be removed. A
+     * history notification for the removal of each operation being disposed
+     * will be sent.
+     * </p>
+     *
+     * @param context
+     *            the context to be disposed
+     * @param flushUndo
+     *            <code>true</code> if the context should be flushed from the
+     *            undo history, <code>false</code> if it should not
+     * @param flushRedo
+     *            <code>true</code> if the context should be flushed from the
+     *            redo history, <code>false</code> if it should not.
+     * @param flushContext
+     *            <code>true</code> if the context is no longer in use and
+     *            references to it should be flushed.
+     */
+    void dispose(IUndoContext context, bool flushUndo, bool flushRedo,
+            bool flushContext);
+
+    /**
+     * <p>
+     * Execute the specified operation and add it to the operations history if
+     * successful. This method is used by clients who wish operation history
+     * listeners to receive notifications before and after the execution of the
+     * operation. Execution of the operation is subject to approval by any
+     * registered {@link IOperationApprover2}. If execution is approved,
+     * listeners will be notified before (<code>ABOUT_TO_EXECUTE</code>) and
+     * after (<code>DONE</code> or <code>OPERATION_NOT_OK</code>).
+     * </p>
+     * <p>
+     * If the operation successfully executes, an additional notification that
+     * the operation has been added to the history (<code>OPERATION_ADDED</code>)
+     * will be sent.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be executed and then added to the history
+     *
+     * @param monitor
+     *            the progress monitor to be used (or <code>null</code>)
+     *            during the operation.
+     *
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the execution succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it was added to the history. <code>OK</code>
+     * severity indicates that the execute operation was successful and that the
+     * operation has been added to the history. Listeners will receive
+     * notifications about the operation's success (<code>DONE</code>) and
+     * about the operation being added to the history (<code>OPERATION_ADDED</code>).
+     * </p>
+     * <p>
+     * <code>CANCEL</code> severity indicates that the user cancelled the
+     * operation and that the operation was not added to the history.
+     * <code>ERROR</code> severity indicates that the operation did not
+     * successfully execute and that it was not added to the history. Any other
+     * severity code is not specifically interpreted by the history, and the
+     * operation will not be added to the history. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>DONE</code> notification if the execution was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     *
+     */
+    IStatus execute(IUndoableOperation operation, IProgressMonitor monitor,
+            IAdaptable info);
+
+    /**
+     * <p>
+     * Return the limit on the undo and redo history for a particular context.
+     * </p>
+     *
+     * @param context
+     *            the context whose limit is requested
+     *
+     * @return the undo and redo history limit for the specified context.
+     */
+    int getLimit(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the array of operations in the redo history for a the specified undo
+     * context. The operations are in the order that they were added to the
+     * history, with the most recently undone operation appearing last in the
+     * array. This history is used LIFO (last in, first out) when successive
+     * "Redo" commands are invoked.
+     *
+     * </p>
+     *
+     * @param context
+     *            the context for the redo
+     * @return the array of operations in the history
+     */
+    IUndoableOperation[] getRedoHistory(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the operation that will next be redone in the given undo context.
+     * </p>
+     *
+     * @param context
+     *            the context for the redo
+     * @return the operation to be redone or <code>null</code> if there is no
+     *         operation available. There is no guarantee that the returned
+     *         operation is valid for redo.
+     */
+    IUndoableOperation getRedoOperation(IUndoContext context);
+
+    /**
+     * <p>
+     * Get the array of operations in the undo history for the specified undo
+     * context. The operations are in the order that they were added to the
+     * history, with the most recently added operation appearing last in the
+     * array. This history is used LIFO (last in, first out) when successive
+     * "Undo" commands are invoked.
+     * </p>
+     *
+     * @param context
+     *            the context for the undo
+     * @return the array of operations in the history
+     */
+    IUndoableOperation[] getUndoHistory(IUndoContext context);
+
+    /**
+     * <p>
+     * Open this composite operation and consider it an operation that contains
+     * other related operations. Consider all operations that are subsequently
+     * executed or added to be part of this operation. When an operation is
+     * opened, listeners will immediately receive a notification for the opened
+     * operation. The specific notification depends on the mode in which the
+     * operation is opened (<code>ABOUT_TO_EXECUTE</code>,
+     * <code>ABOUT_TO_UNDO</code>, <code>ABOUT_TO_REDO</code>).
+     * Notifications for any other execute or add while this operation is open
+     * will not occur. Instead, those operations will be added to the current
+     * operation.
+     * </p>
+     * <p>
+     * Note: This method is intended to be used by legacy undo frameworks that
+     * do not expect related undo operations to appear in the same undo history
+     * as the triggering undo operation. When an operation is open, any
+     * subsequent requests to execute, add, undo, or redo another operation will
+     * result in that operation being added to the open operation. Once the
+     * operation is closed, the composite will be considered an atomic
+     * operation. Clients should not modify the composite directly (by adding
+     * and removing children) while it is open.
+     * </p>
+     * <p>
+     * When a composite is open, operations that are added to the history will
+     * be considered part of the open operation instead. Operations that are
+     * executed while a composite is open will first be executed and then added
+     * to the composite.
+     * </p>
+     * <p>
+     * Open operations cannot be nested. If this method is called when a
+     * different operation is open, it is presumed to be an application coding
+     * error and this method will throw an IllegalStateException.
+     * </p>
+     *
+     * @param operation
+     *            the composite operation to be considered as the parent for all
+     *            subsequent operations.
+     * @param mode
+     *            the mode the operation is executing in. Can be one of
+     *            <code>EXECUTE</code>, <code>UNDO</code>, or
+     *            <code>REDO</code>. This determines what notifications are
+     *            sent.
+     */
+    void openOperation(ICompositeOperation operation, int mode);
+
+    /**
+     * <p>
+     * The specified operation has changed in some way since it was added to the
+     * operation history. Notify listeners with an OPERATION_CHANGED event.
+     * </p>
+     *
+     * @param operation
+     *            the operation that has changed.
+     *
+     */
+    void operationChanged(IUndoableOperation operation);
+
+    /**
+     * <p>
+     * Get the operation that will next be undone in the given undo context.
+     * </p>
+     *
+     * @param context
+     *            the context for the undo
+     * @return the operation to be undone or <code>null</code> if there is no
+     *         operation available. There is no guarantee that the available
+     *         operation is valid for the undo.
+     */
+    IUndoableOperation getUndoOperation(IUndoContext context);
+
+    /**
+     * <p>
+     * Redo the most recently undone operation in the given context. The redo of
+     * the operation is subject to approval by any registered
+     * {@link IOperationApprover} before it is attempted.
+     * </p>
+     *
+     * @param context
+     *            the context to be redone
+     * @param monitor
+     *            the progress monitor to be used for the redo, or
+     *            <code>null</code> if no progress monitor is provided.
+     * @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 dwt.widgets.Shell.class.
+     * @return the IStatus indicating whether the redo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the redo operation was successful and (since
+     * release 3.2), that the operation will be placed in the undo history.
+     * (Prior to 3.2, a successfully redone operation would not be placed on the
+     * undo history if it could not be undone. Since 3.2, this is relaxed, and
+     * all successfully redone operations are placed in the undo history.)
+     * Listeners will receive the <code>REDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>REDONE</code> notification if the redo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     *
+     */
+    IStatus redo(IUndoContext context, IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Redo the specified operation. The redo of the operation is subject to
+     * approval by any registered {@link IOperationApprover} before it is
+     * attempted.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be redone
+     * @param monitor
+     *            the progress monitor to be used for the redo, or code>null</code>
+     *            if no progress monitor is provided
+     * @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
+     *            dwt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the redo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the redo operation was successful, and (since
+     * release 3.2), that the operation will be placed in the undo history.
+     * (Prior to 3.2, a successfully redone operation would not be placed on the
+     * undo history if it could not be undone. Since 3.2, this is relaxed, and
+     * all successfully redone operations are placed in the undo history.)
+     * Listeners will receive the <code>REDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than <code>OK</code>,
+     * listeners will receive the <code>OPERATION_NOT_OK</code> notification
+     * instead of the <code>REDONE</code> notification if the redo was
+     * approved and attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     */
+    IStatus redoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Remove the specified operation approver from the list of operation
+     * approvers that are consulted before an operation is undone or redone.
+     * </p>
+     *
+     * @param approver
+     *            the IOperationApprover to be removed. Must not be
+     *            <code>null</code>. If an attempt is made to remove an
+     *            instance which is not already registered with this instance,
+     *            this method has no effect.
+     */
+    void removeOperationApprover(IOperationApprover approver);
+
+    /**
+     * <p>
+     * Remove the specified listener from the list of operation history
+     * listeners.
+     * </p>
+     *
+     * @param listener
+     *            The IOperationHistoryListener to be removed. Must not be
+     *            <code>null</code>. If an attempt is made to remove an
+     *            instance which is not already registered with this instance,
+     *            this method has no effect.
+     */
+    void removeOperationHistoryListener(IOperationHistoryListener listener);
+
+    /**
+     * <p>
+     * Replace the specified operation in the undo or redo history with the
+     * provided list of replacements. This protocol is typically used when a
+     * composite is broken up into its atomic parts. The replacements will be
+     * inserted so that the first replacement will be the first of the
+     * replacements to be undone or redone. Listeners will be notified about the
+     * removal of the replaced element and the addition of each replacement.
+     * </p>
+     *
+     * @param operation
+     *            The IUndoableOperation to be replaced
+     * @param replacements
+     *            the array of IUndoableOperation to replace the first operation
+     */
+    void replaceOperation(IUndoableOperation operation,
+            IUndoableOperation[] replacements);
+
+    /**
+     * <p>
+     * Set the limit on the undo and redo history for a particular context.
+     * </p>
+     *
+     * @param context
+     *            the context whose limit is being set.
+     *
+     * @param limit
+     *            the maximum number of operations that should be kept in the
+     *            undo or redo history for the specified context. Must not be
+     *            negative.
+     */
+    void setLimit(IUndoContext context, int limit);
+
+    /**
+     * <p>
+     * Undo the most recently executed operation in the given context. The undo
+     * of the operation is subject to approval by any registered
+     * {@link IOperationApprover} before it is attempted.
+     * </p>
+     *
+     * @param context
+     *            the context to be undone
+     * @param monitor
+     *            the progress monitor to be used for the undo, or
+     *            <code>null</code> if no progress monitor is provided.
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the undo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the undo operation was successful, and (since
+     * release 3.2), that the operation will be placed on the redo history.
+     * (Prior to 3.2, a successfully undone operation would not be placed on the
+     * redo history if it could not be redone. Since 3.2, this is relaxed, and
+     * all successfully undone operations are placed in the redo history.)
+     * Listeners will receive the <code>UNDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>UNDONE</code> notification if the undo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+
+    IStatus undo(IUndoContext context, IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Undo the specified operation. The undo of the operation is subject to
+     * approval by any registered {@link IOperationApprover} before it is
+     * attempted.
+     * </p>
+     *
+     * @param operation
+     *            the operation to be undone
+     * @param monitor
+     *            the progress monitor to be used for the undo, or
+     *            <code>null</code> if no progress monitor is provided
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus indicating whether the undo succeeded.
+     *
+     * <p>
+     * The severity code in the returned status describes whether the operation
+     * succeeded and whether it remains in the history. <code>OK</code>
+     * severity indicates that the undo operation was successful, and (since
+     * release 3.2), that the operation will be placed on the redo history.
+     * (Prior to 3.2, a successfully undone operation would not be placed on the
+     * redo history if it could not be redone. Since 3.2, this is relaxed, and
+     * all successfully undone operations are placed in the redo history.)
+     * Listeners will receive the <code>UNDONE</code> notification.
+     * </p>
+     * <p>
+     * Other severity codes (<code>CANCEL</code>, <code>ERROR</code>,
+     * <code>INFO</code>, etc.) are not specifically interpreted by the
+     * history. The operation will remain in the history and the returned status
+     * is simply passed back to the caller. For all severities other than
+     * <code>OK</code>, listeners will receive the
+     * <code>OPERATION_NOT_OK</code> notification instead of the
+     * <code>UNDONE</code> notification if the undo was approved and
+     * attempted.
+     * </p>
+     *
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+    IStatus undoOperation(IUndoableOperation operation,
+            IProgressMonitor monitor, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IOperationHistoryListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.IOperationHistoryListener;
+
+import dwtx.core.commands.operations.OperationHistoryEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * This interface is used to listen to notifications from an IOperationHistory.
+ * The supplied OperationHistoryEvent describes the particular notification.
+ * </p>
+ * <p>
+ * Operation history listeners must be prepared to receive notifications from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IOperationHistoryListener {
+    /**
+     * Something of note has happened in the IOperationHistory. Listeners should
+     * check the supplied event for details.
+     *
+     * @param event
+     *            the OperationHistoryEvent that describes the particular
+     *            notification.
+     */
+    void historyNotification(OperationHistoryEvent event);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IUndoContext.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An undo context is used to "tag" operations as being applicable to a certain
+ * context. The undo context is used to filter the history of operations
+ * available for undo or redo so that only operations appropriate for a given
+ * undo context are shown when the application is presenting that context.
+ * </p>
+ * <p>
+ * The scope of an undo context is defined by the application that is making
+ * undo and redo of operations available. Undo contexts may be related to
+ * application models, or may be associated with UI objects that are providing
+ * undo and redo support.
+ * </p>
+ * <p>
+ * An undo context may be defined as "matching" another context. This allows
+ * applications to provide specialized implementations of an undo context that
+ * will appear in the operation history for their matching context.
+ *
+ * @since 3.1
+ */
+
+public interface IUndoContext {
+
+    /**
+     * Get the label that describes the undo context.
+     *
+     * @return the label for the context.
+     */
+    public String getLabel();
+
+    /**
+     * Return whether the specified context is considered a match for the
+     * receiving context. When a context matches another context, operations
+     * that have the context are considered to also have the matching context.
+     *
+     * @param context
+     *            the context to be checked against the receiving context.
+     *
+     * @return <code>true</code> if the receiving context can be considered a
+     *         match for the specified context, and <code>false</code> if it
+     *         cannot.
+     */
+    public bool matches(IUndoContext context);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/IUndoableOperation.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.IUndoableOperation;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * IUndoableOperation defines an operation that can be executed, undone, and
+ * redone. Operations typically have fully defined parameters. That is, they are
+ * usually created after the user has been queried for any input needed to
+ * define the operation.
+ * </p>
+ * <p>
+ * Operations determine their ability to execute, undo, or redo according to the
+ * current state of the application. They do not make decisions about their
+ * validity based on where they occur in the operation history. That is left to
+ * the particular operation history.
+ * </p>
+ *
+ * @since 3.1
+ */
+public interface IUndoableOperation {
+
+    /**
+     * <p>
+     * Add the specified context to the operation. If a context equal to the
+     * specified context is already present, do not add it again. Note that
+     * determining whether a context is already present is based on equality,
+     * not whether the context matches ({@link IUndoContext#matches(IUndoContext)})
+     * another context.
+     * </p>
+     *
+     * @param context
+     *            the context to be added
+     */
+    void addContext(IUndoContext context);
+
+    /**
+     * <p>
+     * Returns whether the operation can be executed in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual execution of the operation, returning the
+     * appropriate status if the operation cannot actually execute at that time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be executed;
+     *         <code>false</code> otherwise.
+     */
+    bool canExecute();
+
+    /**
+     * <p>
+     * Returns whether the operation can be redone in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual redo of the operation, returning the
+     * appropriate status if the operation cannot actually be redone at that
+     * time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be redone;
+     *         <code>false</code> otherwise.
+     */
+    bool canRedo();
+
+    /**
+     * <p>
+     * Returns whether the operation can be undone in its current state.
+     * </p>
+     *
+     * <p>
+     * Note: The computation for this method must be fast, as it is called
+     * frequently. If necessary, this method can be optimistic in its
+     * computation (returning true) and later perform more time-consuming
+     * computations during the actual undo of the operation, returning the
+     * appropriate status if the operation cannot actually be undone at that
+     * time.
+     * </p>
+     *
+     * @return <code>true</code> if the operation can be undone;
+     *         <code>false</code> otherwise.
+     */
+    bool canUndo();
+
+    /**
+     * Dispose of the operation. This method is used when the operation is no
+     * longer kept in the history. Implementers of this method typically
+     * unregister any listeners.
+     *
+     */
+    void dispose();
+
+    /**
+     * Execute the operation. This method should only be called the first time
+     * that an operation is executed.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus of the execution. The status severity should be set
+     *         to <code>OK</code> if the operation was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the execution.
+     * @throws ExecutionException
+     *             if an exception occurred during execution.
+     */
+    IStatus execute(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * <p>
+     * Returns the array of contexts that have been assigned to the operation.
+     * </p>
+     * <p>
+     * This method may be called by the operation history from inside a
+     * synchronized block. To avoid deadlock conditions, implementers of this
+     * method must avoid dispatching and waiting on threads that modify the
+     * operation history during this method.
+     * </p>
+     *
+     * @return the array of contexts
+     */
+    IUndoContext[] getContexts();
+
+    /**
+     * Return the label that should be used to show the name of the operation to
+     * the user. This label is typically combined with the command strings shown
+     * to the user in "Undo" and "Redo" user interfaces.
+     *
+     * @return the label
+     */
+    String getLabel();
+
+    /**
+     * <p>
+     * Returns whether the operation has a matching context for the specified
+     * context.
+     * </p>
+     * <p>
+     * This method may be called by the operation history from inside a
+     * synchronized block. To avoid deadlock conditions, implementers of this
+     * method must avoid dispatching and waiting on threads that modify the
+     * operation history during this method.
+     * </p>
+     *
+     * @see IUndoContext#matches(IUndoContext)
+     *
+     * @param context
+     *            the context in question
+     * @return <code>true</code> if the context is present, <code>false</code>
+     *         if it is not.
+     */
+    bool hasContext(IUndoContext context);
+
+    /**
+     * Redo the operation. This method should only be called after an operation
+     * has been undone.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @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 dwt.widgets.Shell.class.
+     * @return the IStatus of the redo. The status severity should be set to
+     *         <code>OK</code> if the redo was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the redo.
+     * @throws ExecutionException
+     *             if an exception occurred during redo.
+     */
+
+    IStatus redo(IProgressMonitor monitor, IAdaptable info);
+
+    /**
+     * Remove the specified context from the operation. This method has no
+     * effect if the context is not equal to another context in the context
+     * list. Note that determining whether a context is present when removing it
+     * is based on equality, not whether the context matches ({@link
+     * IUndoContext#matches(IUndoContext)}) another context.
+     *
+     * @param context
+     *            the context to be removed
+     */
+    void removeContext(IUndoContext context);
+
+    /**
+     * Undo the operation. This method should only be called after an operation
+     * has been executed.
+     *
+     * @param monitor
+     *            the progress monitor (or <code>null</code>) to use for
+     *            reporting progress to the user.
+     * @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 dwt.widgets.Shell.class.
+     * @return the IStatus of the undo. The status severity should be set to
+     *         <code>OK</code> if the redo was successful, and
+     *         <code>ERROR</code> if it was not. Any other status is assumed
+     *         to represent an incompletion of the undo.
+     * @throws ExecutionException
+     *             if an exception occurred during undo.
+     */
+    IStatus undo(IProgressMonitor monitor, IAdaptable info);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/LinearUndoEnforcer.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.LinearUndoEnforcer;
+
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+
+import dwtx.core.commands.operations.LinearUndoViolationDetector;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IUndoContext;
+import dwtx.core.commands.operations.IOperationHistory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An operation approver that enforces a strict linear undo. It does not allow
+ * the undo or redo of any operation that is not the latest available operation
+ * in all of its undo contexts.  This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class LinearUndoEnforcer : LinearUndoViolationDetector {
+    /**
+     * Create an instance of LinearUndoEnforcer.
+     */
+    public this() {
+        super();
+    }
+
+    /*
+     * Return whether a linear redo violation is allowable.  A linear redo violation
+     * is defined as a request to redo a particular operation even if it is not the most
+     * recently added operation to the redo history.
+     */
+    protected IStatus allowLinearRedoViolation(IUndoableOperation operation,
+            IUndoContext context, IOperationHistory history, IAdaptable uiInfo) {
+        return Status.CANCEL_STATUS;
+    }
+
+    /*
+     * Return whether a linear undo violation is allowable.  A linear undo violation
+     * is defined as a request to undo a particular operation even if it is not the most
+     * recently added operation to the undo history.
+     */
+    protected IStatus allowLinearUndoViolation(IUndoableOperation operation,
+            IUndoContext context, IOperationHistory history, IAdaptable uiInfo) {
+        return Status.CANCEL_STATUS;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/LinearUndoViolationDetector.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.LinearUndoViolationDetector;
+
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+
+import dwtx.core.commands.operations.IOperationApprover;
+import dwtx.core.commands.operations.IOperationHistory;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An abstract class for detecting violations in a strict linear undo/redo
+ * model. Once a violation is detected, subclasses implement the specific
+ * behavior for indicating whether or not the undo/redo should proceed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class LinearUndoViolationDetector : IOperationApprover {
+
+    /**
+     * Create an instance of LinearUndoViolationDetector.
+     */
+    public this() {
+    }
+
+    /**
+     * Return a status indicating whether a linear redo violation is allowable.
+     * A linear redo violation is defined as a request to redo a particular
+     * operation even if it is not the most recently added operation to the redo
+     * history.
+     *
+     * @param operation
+     *            the operation for which a linear redo violation has been
+     *            detected.
+     * @param context
+     *            the undo context in which the linear redo violation exists
+     * @param history
+     *            the operation history containing the operation
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus describing whether the redo violation is allowed. The
+     *         redo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the redo will be
+     *         returned the status that caused the rejection. Specific status
+     *         severities will not be interpreted by the history.
+     */
+
+    protected abstract IStatus allowLinearRedoViolation(
+            IUndoableOperation operation, IUndoContext context,
+            IOperationHistory history, IAdaptable info);
+
+    /**
+     * Return a status indicating whether a linear undo violation is allowable.
+     * A linear undo violation is defined as a request to undo a particular
+     * operation even if it is not the most recently added operation to the undo
+     * history.
+     *
+     * @param operation
+     *            the operation for which a linear undo violation has been
+     *            detected.
+     * @param context
+     *            the undo context in which the linear undo violation exists
+     * @param history
+     *            the operation history containing the operation
+     * @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 dwt.widgets.Shell.class.
+     *
+     * @return the IStatus describing whether the undo violation is allowed. The
+     *         undo will not proceed if the status severity is not
+     *         <code>OK</code>, and the caller requesting the undo will be
+     *         returned the status that caused the rejection. Specific status
+     *         severities will not be interpreted by the history.
+     */
+    protected abstract IStatus allowLinearUndoViolation(
+            IUndoableOperation operation, IUndoContext context,
+            IOperationHistory history, IAdaptable info);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationApprover#proceedRedoing(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.commands.operations.IOperationHistory,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public final IStatus proceedRedoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            IUndoContext context = contexts[i];
+            if (history.getRedoOperation(context) !is operation) {
+                IStatus status = allowLinearRedoViolation(operation, context,
+                        history, info);
+                if (!status.isOK()) {
+                    return status;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IOperationApprover#proceedUndoing(dwtx.core.commands.operations.IUndoableOperation,
+     *      dwtx.core.commands.operations.IOperationHistory,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+
+    public final IStatus proceedUndoing(IUndoableOperation operation,
+            IOperationHistory history, IAdaptable info) {
+        IUndoContext[] contexts = operation.getContexts();
+        for (int i = 0; i < contexts.length; i++) {
+            IUndoContext context = contexts[i];
+            if (history.getUndoOperation(context) !is operation) {
+                IStatus status = allowLinearUndoViolation(operation, context,
+                        history, info);
+                if (!status.isOK()) {
+                    return status;
+                }
+            }
+        }
+        return Status.OK_STATUS;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/ObjectUndoContext.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.ObjectUndoContext;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwtx.core.commands.operations.UndoContext;
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An undo context that can be used to represent any given object. Clients
+ * can add matching contexts to this context.  This class may be instantiated
+ * by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class ObjectUndoContext : UndoContext {
+
+    private Object object;
+
+    private String label;
+
+    private Seq!(IUndoContext) children;
+
+    /**
+     * Construct an operation context that represents the given object.
+     *
+     * @param object
+     *            the object to be represented.
+     */
+    public this(Object object) {
+        this(object, null);
+    }
+
+    /**
+     * Construct an operation context that represents the given object and has a
+     * specialized label.
+     *
+     * @param object
+     *            the object to be represented.
+     * @param label
+     *            the label for the context
+     */
+    public this(Object object, String label) {
+        this.object = object;
+        this.label = label;
+        children = new ArraySeq!(IUndoContext);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoContext#getLabel()
+     */
+    public String getLabel() {
+        if (label !is null) {
+            return label;
+        }
+        if (object !is null) {
+            return object.toString();
+        }
+        return super.getLabel();
+    }
+
+    /**
+     * Return the object that is represented by this context.
+     *
+     * @return the object represented by this context.
+     */
+    public Object getObject() {
+        return object;
+    }
+
+    /**
+     * Add the specified context as a match of this context. Contexts added as
+     * matches of this context will be interpreted as a match of this context
+     * when the history is filtered for a particular context. Adding a match
+     * allows components to create their own contexts for implementing
+     * specialized behavior, yet have their operations appear in a more
+     * global context.
+     *
+     * @param context
+     *            the context to be added as a match of this context
+     */
+    public void addMatch(IUndoContext context) {
+        children.append(context);
+    }
+
+    /**
+     * Remove the specified context as a match of this context. The context will
+     * no longer be interpreted as a match of this context when the history is
+     * filtered for a particular context. This method has no effect if the
+     * specified context was never previously added as a match.
+     *
+     * @param context
+     *            the context to be removed from the list of matches for this
+     *            context
+     */
+    public void removeMatch(IUndoContext context) {
+        children.remove(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoContext#matches(IUndoContext
+     *      context)
+     */
+    public bool matches(IUndoContext context) {
+        // Check first for explicit matches that have been assigned.
+        if (children.contains(context)) {
+            return true;
+        }
+        // Contexts for equal objects are considered matching
+        if ( null !is cast(ObjectUndoContext)context  && getObject() !is null) {
+            return getObject().opEquals((cast(ObjectUndoContext)context).getObject()) !is 0;
+        }
+        // Use the normal matching implementation
+        return super.matches(context);
+    }
+
+    /**
+     * The string representation of this operation.  Used for debugging purposes only.
+     * This string should not be shown to an end user.
+     *
+     * @return The string representation.
+     */
+    public override String toString() {
+        return getLabel();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/OperationHistoryEvent.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.OperationHistoryEvent;
+
+import dwtx.core.runtime.IStatus;
+import dwtx.core.commands.operations.IOperationHistory;
+import dwtx.core.commands.operations.IUndoableOperation;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * OperationHistoryEvent is used to communicate changes that occur in a
+ * DefaultOperationHistory, including the addition or removal of operations, and
+ * the execution, undo, and redo of operations.
+ * </p>
+ * <p>
+ * Operation history listeners must be prepared to receive notifications from a
+ * background thread. Any UI access occurring inside the implementation must be
+ * properly synchronized using the techniques specified by the client's widget
+ * library.
+ * </p>
+ *
+ *
+ * @since 3.1
+ */
+public final class OperationHistoryEvent {
+
+    /**
+     * ABOUT_TO_EXECUTE indicates that an operation is about to execute.
+     * Listeners should prepare for the execution as appropriate. Listeners will
+     * receive a DONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the execution is cancelled or otherwise
+     * fails. This notification is only received for those operations executed
+     * by the operation history. Operations that are added to the history after
+     * execution do not trigger these notifications.
+     *
+     * If the operation successfully executes, clients will also receive a
+     * notification that it has been added to the history.
+     *
+     * (value is 1).
+     */
+
+    public static const int ABOUT_TO_EXECUTE = 1;
+
+    /**
+     * ABOUT_TO_REDO indicates that an operation is about to be redone.
+     * Listeners should prepare for the redo as appropriate. Listeners will
+     * receive a REDONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the redo is cancelled or otherwise
+     * fails.
+     *
+     * (value is 2).
+     */
+    public static const int ABOUT_TO_REDO = 2;
+
+    /**
+     * ABOUT_TO_UNDO indicates that an operation is about to be undone.
+     * Listeners should prepare for the undo as appropriate. Listeners will
+     * receive an UNDONE notification if the operation is successful, or an
+     * OPERATION_NOT_OK notification if the undo is cancelled or otherwise
+     * fails.
+     *
+     * (value is 3).
+     */
+    public static const int ABOUT_TO_UNDO = 3;
+
+    /**
+     * DONE indicates that an operation has been executed. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI. This
+     * notification is only received for those operations executed by the
+     * operation history. Operations that are added to the history after
+     * execution do not trigger this notification.
+     *
+     * Clients will also receive a notification that the operation has been
+     * added to the history.
+     *
+     * (value is 4).
+     */
+    public static const int DONE = 4;
+
+    /**
+     * OPERATION_ADDED indicates that an operation was added to the history.
+     * Listeners can use this notification to add their undo context to a new
+     * operation as appropriate or otherwise record the operation.
+     *
+     * (value is 5).
+     */
+    public static const int OPERATION_ADDED = 5;
+
+    /**
+     * OPERATION_CHANGED indicates that an operation has changed in some way
+     * since it was added to the operations history.
+     *
+     * (value is 6).
+     */
+    public static const int OPERATION_CHANGED = 6;
+
+    /**
+     * OPERATION_NOT_OK indicates that an operation was attempted and not
+     * successful. Listeners typically use this when they have prepared for an
+     * execute, undo, or redo, and need to know that the operation did not
+     * successfully complete. For example, listeners that turn redraw off before
+     * an operation is undone would turn redraw on when the operation completes,
+     * or when this notification is received, since there will be no
+     * notification of the completion.
+     *
+     * (value is 7).
+     */
+    public static const int OPERATION_NOT_OK = 7;
+
+    /**
+     * OPERATION_REMOVED indicates an operation was removed from the history.
+     * Listeners typically remove any record of the operation that they may have
+     * kept in their own state. The operation has been disposed by the time
+     * listeners receive this notification.
+     *
+     * (value is 8).
+     */
+    public static const int OPERATION_REMOVED = 8;
+
+    /**
+     * REDONE indicates that an operation was redone. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI.
+     *
+     * (value is 9).
+     */
+    public static const int REDONE = 9;
+
+    /**
+     * UNDONE indicates that an operation was undone. Listeners can take
+     * appropriate action, such as revealing any relevant state in the UI.
+     *
+     * (value is 10).
+     */
+    public static const int UNDONE = 10;
+
+    private int code = 0;
+
+    private IOperationHistory history;
+
+    private IUndoableOperation operation;
+
+    /* @since 3.2 */
+    private IStatus status;
+
+    /**
+     * Construct an event for the specified operation history.
+     *
+     * @param code
+     *            the event code to be used.
+     * @param history
+     *            the history triggering the event.
+     * @param operation
+     *            the operation involved in the event.
+     */
+    public this(int code, IOperationHistory history,
+            IUndoableOperation operation) {
+        this(code, history, operation, null);
+    }
+
+    /**
+     * Construct an event for the specified operation history.
+     *
+     * @param code
+     *            the event code to be used.
+     * @param history
+     *            the history triggering the event.
+     * @param operation
+     *            the operation involved in the event.
+     * @param status
+     *            the status associated with the event, or null if no status is
+     *            available.
+     *
+     * @since 3.2
+     */
+    public this(int code, IOperationHistory history,
+            IUndoableOperation operation, IStatus status) {
+        if (history is null) {
+            throw new NullPointerException();
+        }
+        if (operation is null) {
+            throw new NullPointerException();
+        }
+        this.code = code;
+        this.history = history;
+        this.operation = operation;
+        this.status = status;
+    }
+
+    /**
+     * Return the type of event that is occurring.
+     *
+     * @return the type code indicating the type of event.
+     */
+    public int getEventType() {
+        return code;
+    }
+
+    /**
+     * Return the operation history that triggered this event.
+     *
+     * @return the operation history
+     */
+
+    public IOperationHistory getHistory() {
+        return history;
+    }
+
+    /**
+     * Return the operation associated with this event.
+     *
+     * @return the operation
+     */
+
+    public IUndoableOperation getOperation() {
+        return operation;
+    }
+
+    /**
+     * Return the status associated with this event.
+     *
+     * @return the status associated with this event. The status may be null.
+     *
+     * @since 3.2
+     */
+
+    public IStatus getStatus() {
+        return status;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/OperationHistoryFactory.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.OperationHistoryFactory;
+
+import dwtx.core.commands.operations.IOperationHistory;
+import dwtx.core.commands.operations.DefaultOperationHistory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * This class is used to maintain the instance of the operation history that
+ * should be used by classes that access the undo or redo history and add
+ * undoable operations to the history.
+ *
+ * <p>
+ * It is intended that an application can create an operation history appropriate
+ * for its needs and set it into this class.  Otherwise, a default operation history
+ * will be created.  The operation history may only be set one time.  All classes that
+ * access an operations history use this class to obtain the correct instance.
+ *
+ * @since 3.1
+ */
+public final class OperationHistoryFactory {
+
+    private static IOperationHistory operationHistory;
+
+    /**
+     * Return the operation history to be used for managing undoable operations.
+     *
+     * @return the operation history to be used for executing, undoing, and
+     *         redoing operations.
+     */
+    public static IOperationHistory getOperationHistory() {
+        if (operationHistory is null) {
+            operationHistory = new DefaultOperationHistory();
+        }
+        return operationHistory;
+    }
+
+    /**
+     * Set the operation history to be used for managing undoable operations.
+     * This method may only be called one time, and must be called before any
+     * request to get the history.  Attempts to set the operation history will
+     * be ignored after it has been already set, or after a default one has
+     * been created.
+     *
+     * @param history
+     *            the operation history to be used for executing, undoing, and
+     *            redoing operations.
+     */
+    public static void setOperationHistory(IOperationHistory history) {
+        // If one has already been set or created, ignore this request.
+        if (operationHistory is null) {
+            operationHistory = history;
+        }
+    }
+
+    private this() {
+        // may not be instantiated
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/OperationStatus.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.OperationStatus;
+
+import dwtx.core.runtime.Status;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * OperationStatus describes the status of a request to execute, undo, or redo
+ * an operation.  This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class OperationStatus : Status {
+    /**
+     * NOTHING_TO_REDO indicates there was no operation available for redo.
+     *
+     * (value is 1).
+     */
+    public static const int NOTHING_TO_REDO = 1;
+
+    /**
+     * NOTHING_TO_UNDO indicates there was no operation available for undo.
+     *
+     * (value is 2).
+     */
+    public static const int NOTHING_TO_UNDO = 2;
+
+    /**
+     * OPERATION_INVALID indicates that the operation available for undo or redo
+     * is not in a state to successfully perform the undo or redo.
+     *
+     * (value is 3).
+     */
+    public static const int OPERATION_INVALID = 3;
+
+    /**
+     * DEFAULT_PLUGIN_ID identifies the default plugin reporting the status.
+     *
+     * (value is "dwtx.core.commands").
+     */
+    static String DEFAULT_PLUGIN_ID = "dwtx.core.commands"; //$NON-NLS-1$
+
+    /**
+     * Creates a new operation status, specifying all properties.
+     *
+     * @param severity
+     *            the severity for the status
+     * @param pluginId
+     *            the unique identifier of the relevant plug-in
+     * @param code
+     *            the informational code for the status
+     * @param message
+     *            a human-readable message, localized to the current locale
+     * @param exception
+     *            a low-level exception, or <code>null</code> if not
+     *            applicable
+     */
+    public this(int severity, String pluginId, int code, String message, Exception exception) {
+        super(severity, pluginId, code, message, exception);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/TriggeredOperations.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.commands.operations.TriggeredOperations;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+
+import dwtx.core.commands.operations.AbstractOperation;
+import dwtx.core.commands.operations.ICompositeOperation;
+import dwtx.core.commands.operations.IAdvancedUndoableOperation;
+import dwtx.core.commands.operations.IContextReplacingOperation;
+import dwtx.core.commands.operations.IUndoableOperation;
+import dwtx.core.commands.operations.IOperationHistory;
+import dwtx.core.commands.operations.IUndoContext;
+import dwtx.core.commands.operations.OperationHistoryEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Triggered operations are a specialized implementation of a composite
+ * operation that keeps track of operations triggered by the execution of some
+ * primary operation. The composite knows which operation was the trigger for
+ * subsequent operations, and adds all triggered operations as children. When
+ * execution, undo, or redo is performed, only the triggered operation is
+ * executed, undone, or redone if it is still present. If the trigger is removed
+ * from the triggered operations, then the child operations will replace the
+ * triggered operations in the history.
+ * <p>
+ * This class may be instantiated by clients.
+ * </p>
+ *
+ * @since 3.1
+ */
+public final class TriggeredOperations : AbstractOperation,
+        ICompositeOperation, IAdvancedUndoableOperation,
+        IContextReplacingOperation {
+
+    private IUndoableOperation triggeringOperation;
+
+    private IOperationHistory history;
+
+    private Seq!(IUndoableOperation) children;
+
+    /**
+     * Construct a composite triggered operations using the specified undoable
+     * operation as the trigger. Use the label of this trigger as the label of
+     * the operation.
+     *
+     * @param operation
+     *            the operation that will trigger other operations.
+     * @param history
+     *            the operation history containing the triggered operations.
+     */
+    public this(IUndoableOperation operation,
+            IOperationHistory history) {
+        super(operation.getLabel());
+        children = new ArraySeq!(IUndoableOperation);
+        triggeringOperation = operation;
+        recomputeContexts();
+        this.history = history;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#add(dwtx.core.commands.operations.IUndoableOperation)
+     */
+    public void add(IUndoableOperation operation) {
+        children.append(operation);
+        recomputeContexts();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#remove(dwtx.core.commands.operations.IUndoableOperation)
+     */
+    public void remove(IUndoableOperation operation) {
+        if (operation is triggeringOperation) {
+            // the triggering operation is being removed, so we must replace
+            // this composite with its individual triggers.
+            triggeringOperation = null;
+            // save the children before replacing the operation, since this
+            // operation will be disposed as part of replacing it. We don't want
+            // the children to be disposed since they are to replace this
+            // operation.
+            Seq!(IUndoableOperation) childrenToRestore = children.dup;
+            children = new ArraySeq!(IUndoableOperation);
+            recomputeContexts();
+            operation.dispose();
+            // now replace the triggering operation
+            history.replaceOperation(this, childrenToRestore.toArray());
+        } else {
+            children.remove(operation);
+            operation.dispose();
+            recomputeContexts();
+        }
+    }
+
+    /**
+     * Remove the specified context from the receiver. This method is typically
+     * invoked when the history is being flushed for a certain context. In the
+     * case of triggered operations, if the only context for the triggering
+     * operation is being removed, then the triggering operation must be
+     * replaced in the operation history with the atomic operations that it
+     * triggered. If the context being removed is not the only context for the
+     * triggering operation, the triggering operation will remain, and the
+     * children will each be similarly checked.
+     *
+     * @param context
+     *            the undo context being removed from the receiver.
+     */
+    public void removeContext(IUndoContext context) {
+
+        bool recompute = false;
+        // first check to see if we are removing the only context of the
+        // triggering operation
+        if (triggeringOperation !is null
+                && triggeringOperation.hasContext(context)) {
+            if (triggeringOperation.getContexts().length is 1) {
+                remove(triggeringOperation);
+                return;
+            }
+            triggeringOperation.removeContext(context);
+            recompute = true;
+        }
+        // the triggering operation remains, check all the children
+        auto toBeRemoved = new ArraySeq!(IUndoableOperation);
+        for (int i = 0; i < children.size(); i++) {
+            IUndoableOperation child = cast(IUndoableOperation) children.get(i);
+            if (child.hasContext(context)) {
+                if (child.getContexts().length is 1) {
+                    toBeRemoved.append(child);
+                } else {
+                    child.removeContext(context);
+                }
+                recompute = true;
+            }
+        }
+        for (int i = 0; i < toBeRemoved.size(); i++) {
+            remove(cast(IUndoableOperation) toBeRemoved.get(i));
+        }
+        if (recompute) {
+            recomputeContexts();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus execute(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.EXECUTE);
+            try {
+                IStatus status = triggeringOperation.execute(monitor, info);
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.EXECUTE);
+                return status;
+            } catch (ExecutionException e) {
+                history.closeOperation(false, false, IOperationHistory.EXECUTE);
+                throw e;
+            } catch (RuntimeException e) {
+                history.closeOperation(false, false, IOperationHistory.EXECUTE);
+                throw e;
+            }
+
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#redo(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus redo(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.REDO);
+            Seq!(IUndoableOperation) childrenToRestore = children.dup;
+            try {
+                removeAllChildren();
+                IStatus status = triggeringOperation.redo(monitor, info);
+                if (!status.isOK()) {
+                    children = childrenToRestore;
+                }
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.REDO);
+                return status;
+            } catch (ExecutionException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.REDO);
+                throw e;
+            } catch (RuntimeException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.REDO);
+                throw e;
+            }
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#undo(dwtx.core.runtime.IProgressMonitor,
+     *      dwtx.core.runtime.IAdaptable)
+     */
+    public IStatus undo(IProgressMonitor monitor, IAdaptable info) {
+        if (triggeringOperation !is null) {
+            history.openOperation(this, IOperationHistory.UNDO);
+            auto childrenToRestore = children.dup;
+            try {
+                removeAllChildren();
+                IStatus status = triggeringOperation.undo(monitor, info);
+                if (!status.isOK()) {
+                    children = childrenToRestore;
+                }
+                history.closeOperation(status.isOK(), false,
+                        IOperationHistory.UNDO);
+                return status;
+            } catch (ExecutionException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.UNDO);
+                throw e;
+            } catch (RuntimeException e) {
+                children = childrenToRestore;
+                history.closeOperation(false, false, IOperationHistory.UNDO);
+                throw e;
+            }
+        }
+        return IOperationHistory.OPERATION_INVALID_STATUS;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canUndo()
+     */
+    public bool canUndo() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canUndo();
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canExecute()
+     */
+    public bool canExecute() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canExecute();
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IUndoableOperation#canRedo()
+     */
+    public bool canRedo() {
+        if (triggeringOperation !is null) {
+            return triggeringOperation.canRedo();
+        }
+        return false;
+    }
+
+    /*
+     * Dispose all operations in the receiver.
+     */
+    public void dispose() {
+        for (int i = 0; i < children.size(); i++) {
+            children.get(i).dispose();
+        }
+        if (triggeringOperation !is null) {
+            triggeringOperation.dispose();
+        }
+    }
+
+    /*
+     * Recompute contexts in light of some change in the children
+     */
+    private void recomputeContexts() {
+        auto allContexts = new ArraySeq!(IUndoContext);
+        if (triggeringOperation !is null) {
+            IUndoContext[] contexts = triggeringOperation.getContexts();
+            for (int i = 0; i < contexts.length; i++) {
+                allContexts.append(contexts[i]);
+            }
+        }
+        for (int i = 0; i < children.size(); i++) {
+            IUndoContext[] contexts = children.get(i).getContexts();
+            for (int j = 0; j < contexts.length; j++) {
+                if (!allContexts.contains(contexts[j])) {
+                    allContexts.append(contexts[j]);
+                }
+            }
+        }
+        contexts = allContexts;
+
+    }
+
+    /*
+     * Remove all non-triggering children
+     */
+    private void removeAllChildren() {
+        IUndoableOperation[] nonTriggers = children.toArray();
+        for (int i = 0; i < nonTriggers.length; i++) {
+            children.remove(nonTriggers[i]);
+            nonTriggers[i].dispose();
+        }
+    }
+
+    /**
+     * Return the operation that triggered the other operations in this
+     * composite.
+     *
+     * @return the IUndoableOperation that triggered the other children.
+     */
+    public IUndoableOperation getTriggeringOperation() {
+        return triggeringOperation;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IAdvancedModelOperation#getAffectedObjects()
+     */
+    public Object[] getAffectedObjects() {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation  ) {
+            return trg
+                    .getAffectedObjects();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IAdvancedModelOperation#aboutToNotify(dwtx.core.commands.operations.OperationHistoryEvent)
+     */
+    public void aboutToNotify(OperationHistoryEvent event) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            trg.aboutToNotify(event);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(dwtx.core.runtime.IProgressMonitor)
+     */
+    public IStatus computeUndoableStatus(IProgressMonitor monitor) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            try {
+                return trg.computeUndoableStatus(monitor);
+            } catch (OperationCanceledException e) {
+                return Status.CANCEL_STATUS;
+            }
+        }
+        return Status.OK_STATUS;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeRedoableStatus(dwtx.core.runtime.IProgressMonitor)
+     */
+    public IStatus computeRedoableStatus(IProgressMonitor monitor) {
+        if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
+            try {
+                return trg.computeRedoableStatus(monitor);
+            } catch (OperationCanceledException e) {
+                return Status.CANCEL_STATUS;
+            }
+        }
+        return Status.OK_STATUS;
+
+    }
+
+    /**
+     * Replace the undo context of the receiver with the provided replacement
+     * undo context. In the case of triggered operations, all contained
+     * operations are checked and any occurrence of the original context is
+     * replaced with the new undo context.
+     * <p>
+     * This message has no effect if the original undo context is not present in
+     * the receiver.
+     *
+     * @param original
+     *            the undo context which is to be replaced
+     * @param replacement
+     *            the undo context which is replacing the original
+     * @since 3.2
+     */
+    public void replaceContext(IUndoContext original, IUndoContext replacement) {
+
+        // first check the triggering operation
+        if (triggeringOperation !is null
+                && triggeringOperation.hasContext(original)) {
+            if ( auto trg = cast(IContextReplacingOperation)triggeringOperation ) {
+                trg.replaceContext(original, replacement);
+            } else {
+                triggeringOperation.removeContext(original);
+                triggeringOperation.addContext(replacement);
+            }
+        }
+        // Now check all the children
+        for (int i = 0; i < children.size(); i++) {
+            IUndoableOperation child = children.get(i);
+            if (child.hasContext(original)) {
+                if ( auto c = cast(IContextReplacingOperation)child ) {
+                    c.replaceContext(
+                            original, replacement);
+                } else {
+                    child.removeContext(original);
+                    child.addContext(replacement);
+                }
+            }
+        }
+        recomputeContexts();
+    }
+
+    /**
+     * Add the specified context to the operation. Overridden in
+     * TriggeredOperations to add the specified undo context to the triggering
+     * operation.
+     *
+     * @param context
+     *            the context to be added
+     *
+     * @since 3.2
+     */
+    public void addContext(IUndoContext context) {
+        if (triggeringOperation !is null) {
+            triggeringOperation.addContext(context);
+            recomputeContexts();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/operations/UndoContext.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.commands.operations.UndoContext;
+
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * A simple, lightweight undo context that can be used to tag any operation. It
+ * does not provided a specialized label. This class may be instantiated by
+ * clients. This class may also be subclassed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class UndoContext : IUndoContext {
+
+    /**
+     * <p>
+     * Get the label that describes the undo context. The default implementation
+     * returns the empty String. Subclasses may override.
+     * </p>
+     *
+     * @return the label for the context.
+     */
+    public String getLabel() {
+        return ""; //$NON-NLS-1$
+    }
+
+    /**
+     * <p>
+     * Return whether the specified context is considered a match for the
+     * receiving context. When a context matches another context, operations
+     * that have the context are considered to also have the matching context.
+     * The default implementation checks whether the supplied context is
+     * identical to this context. Subclasses may override.
+     * </p>
+     *
+     * @param context
+     *            the context to be checked against the receiving context.
+     *
+     * @return <code>true</code> if the receiving context can be considered a
+     *         match for the specified context, and <code>false</code> if it
+     *         cannot.
+     */
+    public bool matches(IUndoContext context) {
+        return context is this;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/commands/util/Tracing.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.commands.util.Tracing;
+
+import dwt.dwthelper.utils;
+import tango.util.log.Trace;
+
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * <p>
+ * A utility class for printing tracing output to the console.
+ * </p>
+ * <p>
+ * Clients must not extend or instantiate this class.
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class Tracing {
+
+    /**
+     * The separator to place between the component and the message.
+     */
+    public static const String SEPARATOR = " >>> "; //$NON-NLS-1$
+
+    /**
+     * <p>
+     * Prints a tracing message to standard out. The message is prefixed by a
+     * component identifier and some separator. See the example below.
+     * </p>
+     *
+     * <pre>
+     *        BINDINGS &gt;&gt; There are 4 deletion markers
+     * </pre>
+     *
+     * @param component
+     *            The component for which this tracing applies; may be
+     *            <code>null</code>
+     * @param message
+     *            The message to print to standard out; may be <code>null</code>.
+     */
+    public static final void printTrace(String component,
+            String message) {
+        StringBuffer buffer = new StringBuffer();
+        if (component.length !is 0) {
+            buffer.append(component);
+        }
+        if ((component.length !is 0) && (message.length !is 0)) {
+            buffer.append(SEPARATOR);
+        }
+        if (message.length !is 0) {
+            buffer.append(message);
+        }
+        Trace.formatln( "{}", buffer.toString());
+    }
+
+    /**
+     * This class is not intended to be instantiated.
+     */
+    private this() {
+        // Do nothing.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/commands/operations/GlobalUndoContext.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 dwtx.core.internal.commands.operations.GlobalUndoContext;
+
+import dwtx.core.commands.operations.IUndoContext;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>
+ * An operation context that matches to any context.  It can be used to
+ * get an unfiltered (global) history.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class GlobalUndoContext : IUndoContext {
+
+    /* (non-Javadoc)
+     * @see dwtx.core.commands.operations.IUndoContext#getLabel()
+     */
+    public String getLabel() {
+        return "Global Undo Context"; //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.commands.operations.IUndoContext#matches(IUndoContext context)
+     */
+    public bool matches(IUndoContext context) {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/commands/util/Util.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.core.internal.commands.util.Util;
+
+import dwt.dwthelper.utils;
+
+// import java.util.Collections;
+// import java.util.HashMap;
+// import java.util.HashSet;
+// import java.util.Iterator;
+// import java.util.Map;
+// import java.util.Set;
+// import java.util.SortedMap;
+// import java.util.SortedSet;
+// import java.util.TreeMap;
+// import java.util.TreeSet;
+
+/**
+ * A class providing utility functions for the commands plug-in.
+ *
+ * @since 3.1
+ */
+public final class Util {
+/++
+    /**
+     * A shared, unmodifiable, empty, sorted map. This value is guaranteed to
+     * always be the same.
+     */
+    public const static SortedMap EMPTY_SORTED_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap());
+
+    /**
+     * A shared, unmodifiable, empty, sorted set. This value is guaranteed to
+     * always be the same.
+     */
+    public final static SortedSet EMPTY_SORTED_SET = Collections
+            .unmodifiableSortedSet(new TreeSet());
+++/
+    /**
+     * A shared, zero-length string -- for avoiding non-externalized string
+     * tags. This value is guaranteed to always be the same.
+     */
+    public final static String ZERO_LENGTH_STRING = "\0"[ 0 .. 0 ]; //$NON-NLS-1$
+/++
+
+    /**
+     * Asserts the the given object is an instance of the given class --
+     * optionally allowing the object to be <code>null</code>.
+     *
+     * @param object
+     *            The object for which the type should be checked.
+     * @param c
+     *            The class that the object must be; fails if the class is
+     *            <code>null</code>.
+     * @param allowNull
+     *            Whether the object being <code>null</code> will not cause a
+     *            failure.
+     */
+    public static final void assertInstance(final Object object, final Class c,
+            final bool allowNull) {
+        if (object is null && allowNull) {
+            return;
+        }
+
+        if (object is null || c is null) {
+            throw new NullPointerException();
+        } else if (!c.isInstance(object)) {
+            throw new IllegalArgumentException();
+        }
+    }
+++/
+    /**
+     * Compares two bool values. <code>false</code> is considered to be
+     * less than <code>true</code>.
+     *
+     * @param left
+     *            The left value to compare.
+     * @param right
+     *            The right value to compare.
+     * @return <code>-1</code> if <code>left</code> is <code>false</code>
+     *         and <code>right</code> is <code>true</code>;<code>0</code>
+     *         if they are equal; <code>1</code> if <code>left</code> is
+     *         <code>true</code> and <code>right</code> is
+     *         <code>false</code>
+     */
+    public static final int compare(bool left, bool right) {
+        return left is false ? (right is true ? -1 : 0) : (right is true ? 0
+                : 1);
+    }
+
+    /**
+     * Compares two comparable objects, but with protection against
+     * <code>null</code>.
+     *
+     * @param left
+     *            The left value to compare; may be <code>null</code>.
+     * @param right
+     *            The right value to compare; may be <code>null</code>.
+     * @return <code>-1</code> if <code>left</code> is <code>null</code>
+     *         and <code>right</code> is not <code>null</code>;
+     *         <code>0</code> if they are both <code>null</code>;
+     *         <code>1</code> if <code>left</code> is not <code>null</code>
+     *         and <code>right</code> is <code>null</code>. Otherwise, the
+     *         result of <code>left.compareTo(right)</code>.
+     */
+    public static final int compare(Comparable left,
+            Comparable right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            return left.compareTo( cast(Object)right);
+        }
+    }
+
+    /**
+     * Compares two integer values. This method fails if the distance between
+     * <code>left</code> and <code>right</code> is greater than
+     * <code>Integer.MAX_VALUE</code>.
+     *
+     * @param left
+     *            The left value to compare.
+     * @param right
+     *            The right value to compare.
+     * @return <code>left - right</code>
+     */
+    public static final int compare(int left, int right) {
+        return left - right;
+    }
+
+    /**
+     * Compares two objects that are not otherwise comparable. If neither object
+     * is <code>null</code>, then the string representation of each object is
+     * used.
+     *
+     * @param left
+     *            The left value to compare. The string representation of this
+     *            value must not be <code>null</code>.
+     * @param right
+     *            The right value to compare. The string representation of this
+     *            value must not be <code>null</code>.
+     * @return <code>-1</code> if <code>left</code> is <code>null</code>
+     *         and <code>right</code> is not <code>null</code>;
+     *         <code>0</code> if they are both <code>null</code>;
+     *         <code>1</code> if <code>left</code> is not <code>null</code>
+     *         and <code>right</code> is <code>null</code>. Otherwise, the
+     *         result of
+     *         <code>left.toString().compareTo(right.toString())</code>.
+     */
+    public static final int compare(Object left, Object right) {
+        if (left is null && right is null) {
+            return 0;
+        } else if (left is null) {
+            return -1;
+        } else if (right is null) {
+            return 1;
+        } else {
+            return left.toString() < (right.toString());
+        }
+    }
+    public static final int compare(Object[] left, Object[] right) {
+        if (left.length !is right.length ) {
+            return left.length < right.length;
+        }
+        for( int i = 0; i < left.length; i++ ){
+            int res = left[i].opCmp(right[i]);
+            if( res !is 0 ){
+                return res;
+            }
+        }
+        return 0;
+    }
+    public static final int compare(String left, String right) {
+        return left < right;
+    }
+
+    /**
+     * Decides whether two booleans are equal.
+     *
+     * @param left
+     *            The first bool to compare; may be <code>null</code>.
+     * @param right
+     *            The second bool to compare; may be <code>null</code>.
+     * @return <code>true</code> if the booleans are equal; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(bool left, bool right) {
+        return left is right;
+    }
+
+    /**
+     * Decides whether two objects are equal -- defending against
+     * <code>null</code>.
+     *
+     * @param left
+     *            The first object to compare; may be <code>null</code>.
+     * @param right
+     *            The second object to compare; may be <code>null</code>.
+     * @return <code>true</code> if the objects are equals; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(Object left, Object right) {
+        return left is null ? right is null : ((right !is null) && (left
+                .opEquals(right)) !is 0 );
+    }
+    public static bool equals(String left, String right) {
+        return left == right;
+    }
+    public static bool equals(String[] left, String[] right) {
+        if( left.length !is right.length ){
+            return false;
+        }
+        for( int i = 0; i < left.length; i++ ){
+            if( !equals( left[i], right[i] )){
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Tests whether two arrays of objects are equal to each other. The arrays
+     * must not be <code>null</code>, but their elements may be
+     * <code>null</code>.
+     *
+     * @param leftArray
+     *            The left array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @param rightArray
+     *            The right array to compare; may be <code>null</code>, and
+     *            may be empty and may contain <code>null</code> elements.
+     * @return <code>true</code> if the arrays are equal length and the
+     *         elements at the same position are equal; <code>false</code>
+     *         otherwise.
+     */
+    public static bool equals(Object[] leftArray,
+            Object[] rightArray) {
+        if (leftArray is null) {
+            return (rightArray is null);
+        } else if (rightArray is null) {
+            return false;
+        }
+
+        if (leftArray.length !is rightArray.length) {
+            return false;
+        }
+
+        for (int i = 0; i < leftArray.length; i++) {
+            Object left = leftArray[i];
+            Object right = rightArray[i];
+            bool equal = (left is null) ? (right is null) : (left
+                    .opEquals(right) !is 0);
+            if (!equal) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Computes the hash code for an integer.
+     *
+     * @param i
+     *            The integer for which a hash code should be computed.
+     * @return <code>i</code>.
+     */
+    public static final hash_t toHash(int i) {
+        return i;
+    }
+
+    /**
+     * Computes the hash code for an object, but with defense against
+     * <code>null</code>.
+     *
+     * @param object
+     *            The object for which a hash code is needed; may be
+     *            <code>null</code>.
+     * @return The hash code for <code>object</code>; or <code>0</code> if
+     *         <code>object</code> is <code>null</code>.
+     */
+    public static final hash_t toHash( Object object) {
+        return object !is null ? object.toHash() : 0;
+    }
+    public static final hash_t toHash( char[] str) {
+        return str !is null ? dwt.dwthelper.utils.toHash( str ) : 0;
+    }
+/++
+
+    /**
+     * Makes a type-safe copy of the given map. This method should be used when
+     * a map is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param map
+     *            The map which should be copied; must not be <code>null</code>.
+     * @param keyClass
+     *            The class that all the keys must be; must not be
+     *            <code>null</code>.
+     * @param valueClass
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @param allowNullKeys
+     *            Whether <code>null</code> keys should be allowed.
+     * @param allowNullValues
+     *            Whether <code>null</code> values should be allowed.
+     * @return A copy of the map; may be empty, but never <code>null</code>.
+     */
+    public static final Map safeCopy(final Map map, final Class keyClass,
+            final Class valueClass, final bool allowNullKeys,
+            final bool allowNullValues) {
+        if (map is null || keyClass is null || valueClass is null) {
+            throw new NullPointerException();
+        }
+
+        final Map copy = Collections.unmodifiableMap(new HashMap(map));
+        final Iterator iterator = copy.entrySet().iterator();
+
+        while (iterator.hasNext()) {
+            final Map.Entry entry = (Map.Entry) iterator.next();
+            assertInstance(entry.getKey(), keyClass, allowNullKeys);
+            assertInstance(entry.getValue(), valueClass, allowNullValues);
+        }
+
+        return map;
+    }
+
+    /**
+     * Makes a type-safe copy of the given set. This method should be used when
+     * a set is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param set
+     *            The set which should be copied; must not be <code>null</code>.
+     * @param c
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @return A copy of the set; may be empty, but never <code>null</code>.
+     *         None of its element will be <code>null</code>.
+     */
+    public static final Set safeCopy(final Set set, final Class c) {
+        return safeCopy(set, c, false);
+    }
+
+    /**
+     * Makes a type-safe copy of the given set. This method should be used when
+     * a set is crossing an API boundary (i.e., from a hostile plug-in into
+     * internal code, or vice versa).
+     *
+     * @param set
+     *            The set which should be copied; must not be <code>null</code>.
+     * @param c
+     *            The class that all the values must be; must not be
+     *            <code>null</code>.
+     * @param allowNullElements
+     *            Whether null values should be allowed.
+     * @return A copy of the set; may be empty, but never <code>null</code>.
+     */
+    public static final Set safeCopy(final Set set, final Class c,
+            final bool allowNullElements) {
+        if (set is null || c is null) {
+            throw new NullPointerException();
+        }
+
+        final Set copy = Collections.unmodifiableSet(new HashSet(set));
+        final Iterator iterator = copy.iterator();
+
+        while (iterator.hasNext()) {
+            assertInstance(iterator.next(), c, allowNullElements);
+        }
+
+        return set;
+    }
+++/
+    /**
+     * The utility class is meant to just provide static members.
+     */
+    private this() {
+        // Should not be called.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/runtime/IRuntimeConstants.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.runtime.IRuntimeConstants;
+
+import dwt.dwthelper.utils;
+
+public interface IRuntimeConstants {
+
+    /**
+     * The unique identifier constant (value "<code>dwtx.core.runtime</code>")
+     * of the Core Runtime (pseudo-) plug-in.
+     */
+    public static const String PI_RUNTIME = "dwtx.core.runtime"; //$NON-NLS-1$
+
+    /**
+     * Name of this bundle.
+     */
+    public static const String PI_COMMON = "dwtx.equinox.common"; //$NON-NLS-1$
+
+    /**
+     * Status code constant (value 2) indicating an error occurred while running a plug-in.
+     */
+    public static const int PLUGIN_ERROR = 2;
+
+    /**
+     * Status code constant (value 5) indicating the platform could not write
+     * some of its metadata.
+     */
+    public static const int FAILED_WRITE_METADATA = 5;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/runtime/LocalizationUtils.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.runtime.LocalizationUtils;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Helper methods related to string localization.
+ *
+ * @since dwtx.equinox.common 3.3
+ */
+public class LocalizationUtils {
+    /**
+     * This method can be used in the absence of NLS class. The method tries to
+     * use the NLS-based translation routine. If it falls, the method returns the original
+     * non-translated key.
+     *
+     * @param key case-sensetive name of the filed in the translation file representing
+     * the string to be translated
+     * @return
+     */
+    static public String safeLocalize(String key) {
+//TODO: LocalizationUtils tries to load module CommonMessages. How to handle this?
+//         try {
+//             Class messageClass = Class.forName("dwtx.core.internal.runtime.CommonMessages"); //$NON-NLS-1$
+//             if (messageClass is null)
+//                 return key;
+//             Field field = messageClass.getDeclaredField(key);
+//             if (field is null)
+//                 return key;
+//             Object value = field.get(null);
+//             if (value instanceof String)
+//                 return (String) value;
+//         } catch (ClassNotFoundException e) {
+//             // eat exception and fall through
+//         } catch (NoClassDefFoundError e) {
+//             // eat exception and fall through
+//         } catch (SecurityException e) {
+//             // eat exception and fall through
+//         } catch (NoSuchFieldException e) {
+//             // eat exception and fall through
+//         } catch (IllegalArgumentException e) {
+//             // eat exception and fall through
+//         } catch (IllegalAccessException e) {
+//             // eat exception and fall through
+//         }
+        return key;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/Assert.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.Assert;
+
+import dwtx.core.runtime.AssertionFailedException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code. The predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (and certainly should not being catching them
+ * specifically).
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be instantiated or sub-classed by clients.
+ * </p>
+ * @since dwtx.equinox.common 3.2
+ */
+public final class Assert {
+    /* This class is not intended to be instantiated. */
+    private this() {
+        // not allowed
+    }
+
+    /** Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     *
+     * @param expression the outcode of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression) {
+        return isLegal(expression, ""); //$NON-NLS-1$
+    }
+
+    /** Asserts that an argument is legal. If the given bool is
+     * not <code>true</code>, an <code>IllegalArgumentException</code>
+     * is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcode of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     * @exception IllegalArgumentException if the legality test failed
+     */
+    public static bool isLegal(bool expression, String message) {
+        if (!expression)
+            throw new IllegalArgumentException(message);
+        return expression;
+    }
+
+    /** Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param object the value to test
+     */
+    public static void isNotNull(Object object) {
+        isNotNull(object, ""); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given object is not <code>null</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param object the value to test
+     * @param message the message to include in the exception
+     */
+    public static void isNotNull(Object object, String message) {
+        if (object is null)
+            throw new AssertionFailedException("null argument:" ~ message); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     *
+     * @param expression the outcode of the check
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression) {
+        return isTrue(expression, ""); //$NON-NLS-1$
+    }
+
+    /** Asserts that the given bool is <code>true</code>. If this
+     * is not the case, some kind of unchecked exception is thrown.
+     * The given message is included in that exception, to aid debugging.
+     *
+     * @param expression the outcode of the check
+     * @param message the message to include in the exception
+     * @return <code>true</code> if the check passes (does not return
+     *    if the check fails)
+     */
+    public static bool isTrue(bool expression, String message) {
+        if (!expression)
+            throw new AssertionFailedException("assertion failed: " ~ message); //$NON-NLS-1$
+        return expression;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/AssertionFailedException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.AssertionFailedException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <code>AssertionFailedException</code> is a runtime exception thrown
+ * by some of the methods in <code>Assert</code>.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be instantiated or sub-classed by clients.
+ * </p>
+ * @see Assert
+ * @since dwtx.equinox.common 3.2
+ */
+public class AssertionFailedException : RuntimeException {
+
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructs a new exception with the given message.
+     *
+     * @param detail the message
+     */
+    public this(String detail) {
+        super(detail);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/CoreException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.CoreException;
+
+import dwtx.core.runtime.IStatus;
+
+// import java.io.PrintStream;
+// import java.io.PrintWriter;
+
+import dwt.dwthelper.utils;
+import tango.io.Stdout;
+
+/**
+ * A checked exception representing a failure.
+ * <p>
+ * Core exceptions contain a status object describing the
+ * cause of the exception.
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @see IStatus
+ */
+public class CoreException : Exception {
+
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static const long serialVersionUID = 1L;
+
+    /** Status object. */
+    private IStatus status;
+
+    /**
+     * Creates a new exception with the given status object.  The message
+     * of the given status is used as the exception message.
+     *
+     * @param status the status object to be associated with this exception
+     */
+    public this(IStatus status) {
+        super(status.getMessage());
+        this.status = status;
+    }
+
+    /**
+     * Returns the status object for this exception.
+     *
+     * @return a status object
+     */
+    public final IStatus getStatus() {
+        return status;
+    }
+
+    /**
+     * Prints a stack trace out for the exception, and
+     * any nested exception that it may have embedded in
+     * its Status object.
+     */
+    public void printStackTrace() {
+//         printStackTrace(System.err);
+        Stderr.formatln( "Exception in File {}({}): {}", this.file, this.line, this.msg );
+        foreach( msg; this.info ){
+            Stderr.formatln( "    trc: {}", msg );
+        }
+        if (status.getException() !is null) {
+            Stderr.formatln( "{}[{}]: ", this.classinfo.name, status.getCode() ); //$NON-NLS-1$ //$NON-NLS-2$
+//             status.getException().printStackTrace();
+                auto e = status.getException();
+                Stderr.formatln( "Exception in File {}({}): {}", e.file, e.line, e.msg );
+                foreach( msg; e.info ){
+                    Stderr.formatln( "    trc: {}", msg );
+                }
+        }
+    }
+
+//FIXME
+//     /**
+//      * Prints a stack trace out for the exception, and
+//      * any nested exception that it may have embedded in
+//      * its Status object.
+//      *
+//      * @param output the stream to write to
+//      */
+//     public void printStackTrace(PrintStream output) {
+//         synchronized (output) {
+//             super.printStackTrace(output);
+//             if (status.getException() !is null) {
+//                 output.print(getClass().getName() + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+//                 status.getException().printStackTrace(output);
+//             }
+//         }
+//     }
+//
+//     /**
+//      * Prints a stack trace out for the exception, and
+//      * any nested exception that it may have embedded in
+//      * its Status object.
+//      *
+//      * @param output the stream to write to
+//      */
+//     public void printStackTrace(PrintWriter output) {
+//         synchronized (output) {
+//             super.printStackTrace(output);
+//             if (status.getException() !is null) {
+//                 output.print(getClass().getName() + "[" + status.getCode() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$
+//                 status.getException().printStackTrace(output);
+//             }
+//         }
+//     }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IAdaptable.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IAdaptable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An interface for an adaptable object.
+ * <p>
+ * Adaptable objects can be dynamically extended to provide different
+ * interfaces (or "adapters").  Adapters are created by adapter
+ * factories, which are in turn managed by type by adapter managers.
+ * </p>
+ * For example,
+ * <pre>
+ *     IAdaptable a = [some adaptable];
+ *     IFoo x = (IFoo)a.getAdapter(IFoo.class);
+ *     if (x !is null)
+ *         [do IFoo things with x]
+ * </pre>
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface, or obtain a default implementation
+ * of this interface by subclassing <code>PlatformObject</code>.
+ * </p>
+ * @see IAdapterFactory
+ * @see IAdapterManager
+ * @see PlatformObject
+ */
+public interface IAdaptable {
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with this object. Returns <code>null</code> if
+     * no such object can be found.
+     *
+     * @param adapter the adapter class to look up
+     * @return a object castable to the given class,
+     *    or <code>null</code> if this object does not
+     *    have an adapter for the given class
+     */
+    public Object getAdapter(ClassInfo adapter);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IAdapterFactory.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IAdapterFactory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An adapter factory defines behavioral extensions for
+ * one or more classes that implements the <code>IAdaptable</code>
+ * interface. Adapter factories are registered with an
+ * adapter manager.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IAdapterManager
+ * @see IAdaptable
+ */
+public interface IAdapterFactory {
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with the given object. Returns <code>null</code> if
+     * no such object can be found.
+     *
+     * @param adaptableObject the adaptable object being queried
+     *   (usually an instance of <code>IAdaptable</code>)
+     * @param adapterType the type of adapter to look up
+     * @return a object castable to the given adapter type,
+     *    or <code>null</code> if this adapter factory
+     *    does not have an adapter of the given type for the
+     *    given object
+     */
+    public Object getAdapter(Object adaptableObject, ClassInfo adapterType);
+
+    /**
+     * Returns the collection of adapter types handled by this
+     * factory.
+     * <p>
+     * This method is generally used by an adapter manager
+     * to discover which adapter types are supported, in advance
+     * of dispatching any actual <code>getAdapter</code> requests.
+     * </p>
+     *
+     * @return the collection of adapter types
+     */
+    public ClassInfo[] getAdapterList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IAdapterManager.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IAdapterManager;
+
+import dwtx.core.runtime.IAdapterFactory;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An adapter manager maintains a registry of adapter factories. Clients
+ * directly invoke methods on an adapter manager to register and unregister
+ * adapters. All adaptable objects (that is, objects that implement the <code>IAdaptable</code>
+ * interface) funnel <code>IAdaptable.getAdapter</code> invocations to their
+ * adapter manager's <code>IAdapterManger.getAdapter</code> method. The
+ * adapter manager then forwards this request unmodified to the <code>IAdapterFactory.getAdapter</code>
+ * method on one of the registered adapter factories.
+ * <p>
+ * Adapter factories can be registered programmatically using the <code>registerAdapters</code>
+ * method.  Alternatively, they can be registered declaratively using the
+ * <code>dwtx.core.runtime.adapters</code> extension point.  Factories registered
+ * with this extension point will not be able to provide adapters until their
+ * corresponding plugin has been activated.
+ * <p>
+ * The following code snippet shows how one might register an adapter of type
+ * <code>com.example.acme.Sticky</code> on resources in the workspace.
+ * <p>
+ *
+ * <pre>
+ *  IAdapterFactory pr = new IAdapterFactory() {
+ *      public Class[] getAdapterList() {
+ *          return new Class[] { com.example.acme.Sticky.class };
+ *      }
+ *      public Object getAdapter(Object adaptableObject, Class adapterType) {
+ *          IResource res = (IResource) adaptableObject;
+ *          QualifiedName key = new QualifiedName(&quot;com.example.acme&quot;, &quot;sticky-note&quot;);
+ *          try {
+ *              com.example.acme.Sticky v = (com.example.acme.Sticky) res.getSessionProperty(key);
+ *              if (v is null) {
+ *                  v = new com.example.acme.Sticky();
+ *                  res.setSessionProperty(key, v);
+ *              }
+ *          } catch (CoreException e) {
+ *              // unable to access session property - ignore
+ *          }
+ *          return v;
+ *      }
+ *  }
+ *  Platform.getAdapterManager().registerAdapters(pr, IResource.class);
+ *   </pre>
+ *
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ * @see IAdaptable
+ * @see IAdapterFactory
+ */
+public interface IAdapterManager {
+
+    /**
+     * This value can be returned to indicate that no applicable adapter factory
+     * was found.
+     * @since dwtx.equinox.common 3.3
+     */
+    public static const int NONE = 0;
+
+    /**
+     * This value can be returned to indicate that an adapter factory was found,
+     * but has not been loaded.
+     * @since dwtx.equinox.common 3.3
+     */
+    public static const int NOT_LOADED = 1;
+
+    /**
+     * This value can be returned to indicate that an adapter factory is loaded.
+     * @since dwtx.equinox.common 3.3
+     */
+    public static const int LOADED = 2;
+
+    /**
+     * Returns the types that can be obtained by converting <code>adaptableClass</code>
+     * via this manager. Converting means that subsequent calls to <code>getAdapter()</code>
+     * or <code>loadAdapter()</code> could result in an adapted object.
+     * <p>
+     * Note that the returned types do not guarantee that
+     * a subsequent call to <code>getAdapter</code> with the same type as an argument
+     * will return a non-null result. If the factory's plug-in has not yet been
+     * loaded, or if the factory itself returns <code>null</code>, then
+     * <code>getAdapter</code> will still return <code>null</code>.
+     * </p>
+     * @param adaptableClass the adaptable class being queried
+     * @return an array of type names that can be obtained by converting
+     * <code>adaptableClass</code> via this manager. An empty array
+     * is returned if there are none.
+     * @since 3.1
+     */
+    public String[] computeAdapterTypes(ClassInfo adaptableClass);
+
+    /**
+     * Returns the class search order for a given class. The search order from a
+     * class with the definition <br>
+     * <code>class X extends Y implements A, B</code><br>
+     * is as follows:
+     * <ul>
+     * <li>the target's class: X
+     * <li>X's superclasses in order to <code>Object</code>
+     * <li>a breadth-first traversal of the target class's interfaces in the
+     * order returned by <code>getInterfaces</code> (in the example, A and its
+     * superinterfaces then B and its superinterfaces) </li>
+     * </ul>
+     *
+     * @param clazz the class for which to return the class order.
+     * @return the class search order for the given class. The returned
+     * search order will minimally  contain the target class.
+     * @since 3.1
+     */
+    public ClassInfo[] computeClassOrder(ClassInfo clazz);
+
+    /**
+     * Returns an object which is an instance of the given class associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that this method will never cause plug-ins to be loaded. If the
+     * only suitable factory is not yet loaded, this method will return <code>null</code>.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterType the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     */
+    public Object getAdapter(Object adaptable, ClassInfo adapterType);
+
+    /**
+     * Returns an object which is an instance of the given class name associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that this method will never cause plug-ins to be loaded. If the
+     * only suitable factory is not yet loaded, this method will return <code>null</code>.
+     * If activation of the plug-in providing the factory is required, use the
+     * <code>loadAdapter</code> method instead.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified name of the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     * @since 3.0
+     */
+    public Object getAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns whether there is an adapter factory registered that may be able
+     * to convert <code>adaptable</code> to an object of type <code>adapterTypeName</code>.
+     * <p>
+     * Note that a return value of <code>true</code> does not guarantee that
+     * a subsequent call to <code>getAdapter</code> with the same arguments
+     * will return a non-null result. If the factory's plug-in has not yet been
+     * loaded, or if the factory itself returns <code>null</code>, then
+     * <code>getAdapter</code> will still return <code>null</code>.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified class name of an adapter to
+     * look up
+     * @return <code>true</code> if there is an adapter factory that claims
+     * it can convert <code>adaptable</code> to an object of type <code>adapterType</code>,
+     * and <code>false</code> otherwise.
+     * @since 3.0
+     */
+    public bool hasAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns a status of an adapter factory registered that may be able
+     * to convert <code>adaptable</code> to an object of type <code>adapterTypeName</code>.
+     * <p>
+     * One of the following values can be returned:<ul>
+     * <li>{@link dwtx.core.runtime.IAdapterManager#NONE} if no applicable adapter factory was found;</li>
+     * <li>{@link dwtx.core.runtime.IAdapterManager#NOT_LOADED} if an adapter factory was found, but has not been loaded;</li>
+     * <li>{@link dwtx.core.runtime.IAdapterManager#LOADED} if an adapter factory was found, and it is loaded.</li>
+     * </ul></p>
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified class name of an adapter to
+     * look up
+     * @return a status of the adapter
+     * @since dwtx.equinox.common 3.3
+     */
+    public int queryAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Returns an object that is an instance of the given class name associated
+     * with the given object. Returns <code>null</code> if no such object can
+     * be found.
+     * <p>
+     * Note that unlike the <code>getAdapter</code> methods, this method
+     * will cause the plug-in that contributes the adapter factory to be loaded
+     * if necessary. As such, this method should be used judiciously, in order
+     * to avoid unnecessary plug-in activations. Most clients should avoid
+     * activation by using <code>getAdapter</code> instead.
+     *
+     * @param adaptable the adaptable object being queried (usually an instance
+     * of <code>IAdaptable</code>)
+     * @param adapterTypeName the fully qualified name of the type of adapter to look up
+     * @return an object castable to the given adapter type, or <code>null</code>
+     * if the given adaptable object does not have an available adapter of the
+     * given type
+     * @since 3.0
+     */
+    public Object loadAdapter(Object adaptable, String adapterTypeName);
+
+    /**
+     * Registers the given adapter factory as extending objects of the given
+     * type.
+     * <p>
+     * If the type being extended is a class, the given factory's adapters are
+     * available on instances of that class and any of its subclasses. If it is
+     * an interface, the adapters are available to all classes that directly or
+     * indirectly implement that interface.
+     * </p>
+     *
+     * @param factory the adapter factory
+     * @param adaptable the type being extended
+     * @see #unregisterAdapters(IAdapterFactory)
+     * @see #unregisterAdapters(IAdapterFactory, Class)
+     */
+    public void registerAdapters(IAdapterFactory factory, ClassInfo adaptable);
+
+    /**
+     * Removes the given adapter factory completely from the list of registered
+     * factories. Equivalent to calling <code>unregisterAdapters(IAdapterFactory,Class)</code>
+     * on all classes against which it had been explicitly registered. Does
+     * nothing if the given factory is not currently registered.
+     *
+     * @param factory the adapter factory to remove
+     * @see #registerAdapters(IAdapterFactory, Class)
+     */
+    public void unregisterAdapters(IAdapterFactory factory);
+
+    /**
+     * Removes the given adapter factory from the list of factories registered
+     * as extending the given class. Does nothing if the given factory and type
+     * combination is not registered.
+     *
+     * @param factory the adapter factory to remove
+     * @param adaptable one of the types against which the given factory is
+     * registered
+     * @see #registerAdapters(IAdapterFactory, Class)
+     */
+    public void unregisterAdapters(IAdapterFactory factory, ClassInfo adaptable);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/ILogListener.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.ILogListener;
+
+import dwtx.core.runtime.IStatus;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A log listener is notified of entries added to a plug-in's log.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see ILog#addLogListener(ILogListener)
+ * @see Platform#addLogListener(ILogListener)
+ */
+public interface ILogListener : EventListener {
+    /**
+     * Notifies this listener that given status has been logged by
+     * a plug-in.  The listener is free to retain or ignore this status.
+     *
+     * @param status the status being logged
+     * @param plugin the plugin of the log which generated this event
+     */
+    public void logging(IStatus status, String plugin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IPath.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,510 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IPath;
+
+import dwt.dwthelper.utils;
+import tango.io.FilePath;
+
+/**
+ * A path is an ordered collection of string segments,
+ * separated by a standard separator character, "/".
+ * A path may also have a leading and/or a trailing separator.
+ * Paths may also be prefixed by an optional device id, which includes
+ * the character(s) which separate the device id from the rest
+ * of the path. For example, "C:" and "Server/Volume:" are typical
+ * device ids.
+ * A device independent path has <code>null</code> for a device id.
+ * <p>
+ * Note that paths are value objects; all operations on paths
+ * return a new path; the path that is operated on is unscathed.
+ * </p>
+ * <p>
+ * UNC paths are denoted by leading double-slashes such
+ * as <code>//Server/Volume/My/Path</code>. When a new path
+ * is constructed all double-slashes are removed except those
+ * appearing at the beginning of the path.
+ * </p>
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ * @see Path
+ */
+public interface IPath : Cloneable {
+
+    /**
+     * Path separator character constant "/" used in paths.
+     */
+    public static const char SEPARATOR = '/';
+
+    /**
+     * Device separator character constant ":" used in paths.
+     */
+    public static const char DEVICE_SEPARATOR = ':';
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the given file extension added.  If this path is empty, root or has a
+     * trailing separator, this path is returned.  If this path already
+     * has an extension, the existing extension is left and the given
+     * extension simply appended.  Clients wishing to replace
+     * the current extension should first remove the extension and
+     * then add the desired one.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * The given extension should not include a leading ".".
+     * </p>
+     *
+     * @param extension the file extension to append
+     * @return the new path
+     */
+    public IPath addFileExtension(String extension);
+
+    /**
+     * Returns a path with the same segments as this path
+     * but with a trailing separator added.
+     * This path must have at least one segment.
+     * <p>
+     * If this path already has a trailing separator,
+     * this path is returned.
+     * </p>
+     *
+     * @return the new path
+     * @see #hasTrailingSeparator()
+     * @see #removeTrailingSeparator()
+     */
+    public IPath addTrailingSeparator();
+
+    /**
+     * Returns the canonicalized path obtained from the
+     * concatenation of the given string path to the
+     * end of this path. The given string path must be a valid
+     * path. If it has a trailing separator,
+     * the result will have a trailing separator.
+     * The device id of this path is preserved (the one
+     * of the given string is ignored). Duplicate slashes
+     * are removed from the path except at the beginning
+     * where the path is considered to be UNC.
+     *
+     * @param path the string path to concatenate
+     * @return the new path
+     * @see #isValidPath(String)
+     */
+    public IPath append(String path);
+
+    /**
+     * Returns the canonicalized path obtained from the
+     * concatenation of the given path's segments to the
+     * end of this path.  If the given path has a trailing
+     * separator, the result will have a trailing separator.
+     * The device id of this path is preserved (the one
+     * of the given path is ignored). Duplicate slashes
+     * are removed from the path except at the beginning
+     * where the path is considered to be UNC.
+     *
+     * @param path the path to concatenate
+     * @return the new path
+     */
+    public IPath append(IPath path);
+
+    /**
+     * Returns a copy of this path.
+     *
+     * @return the cloned path
+     */
+    public Object clone();
+
+    /**
+     * Returns whether this path equals the given object.
+     * <p>
+     * Equality for paths is defined to be: same sequence of segments,
+     * same absolute/relative status, and same device.
+     * Trailing separators are disregarded.
+     * Paths are not generally considered equal to objects other than paths.
+     * </p>
+     *
+     * @param obj the other object
+     * @return <code>true</code> if the paths are equivalent,
+     *    and <code>false</code> if they are not
+     */
+    public int opEquals(Object obj);
+
+    /**
+     * Returns the device id for this path, or <code>null</code> if this
+     * path has no device id. Note that the result will end in ':'.
+     *
+     * @return the device id, or <code>null</code>
+     * @see #setDevice(String)
+     */
+    public String getDevice();
+
+    /**
+     * Returns the file extension portion of this path,
+     * or <code>null</code> if there is none.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * If there is no period in the last segment, the path has no
+     * file extension portion. If the last segment ends in a period,
+     * the file extension portion is the empty string.
+     * </p>
+     *
+     * @return the file extension or <code>null</code>
+     */
+    public String getFileExtension();
+
+    /**
+     * Returns whether this path has a trailing separator.
+     * <p>
+     * Note: In the root path ("/"), the separator is considered to
+     * be leading rather than trailing.
+     * </p>
+     *
+     * @return <code>true</code> if this path has a trailing
+     *    separator, and <code>false</code> otherwise
+     * @see #addTrailingSeparator()
+     * @see #removeTrailingSeparator()
+     */
+    public bool hasTrailingSeparator();
+
+    /**
+     * Returns whether this path is an absolute path (ignoring
+     * any device id).
+     * <p>
+     * Absolute paths start with a path separator.
+     * A root path, like <code>/</code> or <code>C:/</code>,
+     * is considered absolute.  UNC paths are always absolute.
+     * </p>
+     *
+     * @return <code>true</code> if this path is an absolute path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isAbsolute();
+
+    /**
+     * Returns whether this path has no segments and is not
+     * a root path.
+     *
+     * @return <code>true</code> if this path is empty,
+     *    and <code>false</code> otherwise
+     */
+    public bool isEmpty();
+
+    /**
+     * Returns whether this path is a prefix of the given path.
+     * To be a prefix, this path's segments must
+     * appear in the argument path in the same order,
+     * and their device ids must match.
+     * <p>
+     * An empty path is a prefix of all paths with the same device; a root path is a prefix of
+     * all absolute paths with the same device.
+     * </p>
+     * @param anotherPath the other path
+     * @return <code>true</code> if this path is a prefix of the given path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isPrefixOf(IPath anotherPath);
+
+    /**
+     * Returns whether this path is a root path.
+     * <p>
+     * The root path is the absolute non-UNC path with zero segments;
+     * e.g., <code>/</code> or <code>C:/</code>.
+     * The separator is considered a leading separator, not a trailing one.
+     * </p>
+     *
+     * @return <code>true</code> if this path is a root path,
+     *    and <code>false</code> otherwise
+     */
+    public bool isRoot();
+
+    /**
+     * Returns a bool value indicating whether or not this path
+     * is considered to be in UNC form. Return false if this path
+     * has a device set or if the first 2 characters of the path string
+     * are not <code>Path.SEPARATOR</code>.
+     *
+     * @return bool indicating if this path is UNC
+     */
+    public bool isUNC();
+
+    /**
+     * Returns whether the given string is syntactically correct as
+     * a path.  The device id is the prefix up to and including the device
+     * separator for the local file system; the path proper is everything to
+     * the right of it, or the entire string if there is no device separator.
+     * When the platform location is a file system with no meaningful device
+     * separator, the entire string is treated as the path proper.
+     * The device id is not checked for validity; the path proper is correct
+    * if each of the segments in its canonicalized form is valid.
+     *
+     * @param path the path to check
+     * @return <code>true</code> if the given string is a valid path,
+     *    and <code>false</code> otherwise
+     * @see #isValidSegment(String)
+     */
+    public bool isValidPath(String path);
+
+    /**
+     * Returns whether the given string is valid as a segment in
+     * a path. The rules for valid segments are as follows:
+     * <ul>
+     * <li> the empty string is not valid
+     * <li> any string containing the slash character ('/') is not valid
+     * <li>any string containing segment or device separator characters
+     * on the local file system, such as the backslash ('\') and colon (':')
+     * on some file systems.
+     * </ul>
+     *
+     * @param segment the path segment to check
+     * @return <code>true</code> if the given path segment is valid,
+     *    and <code>false</code> otherwise
+     */
+    public bool isValidSegment(String segment);
+
+    /**
+     * Returns the last segment of this path, or
+     * <code>null</code> if it does not have any segments.
+     *
+     * @return the last segment of this path, or <code>null</code>
+     */
+    public String lastSegment();
+
+    /**
+     * Returns an absolute path with the segments and device id of this path.
+     * Absolute paths start with a path separator. If this path is absolute,
+     * it is simply returned.
+     *
+     * @return the new path
+     */
+    public IPath makeAbsolute();
+
+    /**
+     * Returns a relative path with the segments and device id of this path.
+     * Absolute paths start with a path separator and relative paths do not.
+     * If this path is relative, it is simply returned.
+     *
+     * @return the new path
+     */
+    public IPath makeRelative();
+
+    /**
+     * Return a new path which is the equivalent of this path converted to UNC
+     * form (if the given bool is true) or this path not as a UNC path (if the given
+     * bool is false). If UNC, the returned path will not have a device and the
+     * first 2 characters of the path string will be <code>Path.SEPARATOR</code>. If not UNC, the
+     *  first 2 characters of the returned path string will not be <code>Path.SEPARATOR</code>.
+     *
+     * @param toUNC true if converting to UNC, false otherwise
+     * @return the new path, either in UNC form or not depending on the bool parameter
+     */
+    public IPath makeUNC(bool toUNC);
+
+    /**
+     * Returns a count of the number of segments which match in
+     * this path and the given path (device ids are ignored),
+     * comparing in increasing segment number order.
+     *
+     * @param anotherPath the other path
+     * @return the number of matching segments
+     */
+    public int matchingFirstSegments(IPath anotherPath);
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the file extension removed.  If this path does not have an
+     * extension, this path is returned.
+     * <p>
+     * The file extension portion is defined as the string
+     * following the last period (".") character in the last segment.
+     * If there is no period in the last segment, the path has no
+     * file extension portion. If the last segment ends in a period,
+     * the file extension portion is the empty string.
+     * </p>
+     *
+     * @return the new path
+     */
+    public IPath removeFileExtension();
+
+    /**
+     * Returns a copy of this path with the given number of segments
+     * removed from the beginning. The device id is preserved.
+     * The number must be greater or equal zero.
+     * If the count is zero, this path is returned.
+     * The resulting path will always be a relative path with respect
+     * to this path.  If the number equals or exceeds the number
+     * of segments in this path, an empty relative path is returned.
+     *
+     * @param count the number of segments to remove
+     * @return the new path
+     */
+    public IPath removeFirstSegments(int count);
+
+    /**
+     * Returns a copy of this path with the given number of segments
+     * removed from the end. The device id is preserved.
+     * The number must be greater or equal zero.
+     * If the count is zero, this path is returned.
+     * <p>
+     * If this path has a trailing separator, it will still
+     * have a trailing separator after the last segments are removed
+     * (assuming there are some segments left).  If there is no
+     * trailing separator, the result will not have a trailing
+     * separator.
+     * If the number equals or exceeds the number
+     * of segments in this path, a path with no segments is returned.
+     * </p>
+     *
+     * @param count the number of segments to remove
+     * @return the new path
+     */
+    public IPath removeLastSegments(int count);
+
+    /**
+     * Returns a path with the same segments as this path
+     * but with a trailing separator removed.
+     * Does nothing if this path does not have at least one segment.
+     * The device id is preserved.
+     * <p>
+     * If this path does not have a trailing separator,
+     * this path is returned.
+     * </p>
+     *
+     * @return the new path
+     * @see #addTrailingSeparator()
+     * @see #hasTrailingSeparator()
+     */
+    public IPath removeTrailingSeparator();
+
+    /**
+     * Returns the specified segment of this path, or
+     * <code>null</code> if the path does not have such a segment.
+     *
+     * @param index the 0-based segment index
+     * @return the specified segment, or <code>null</code>
+     */
+    public String segment(int index);
+
+    /**
+     * Returns the number of segments in this path.
+     * <p>
+     * Note that both root and empty paths have 0 segments.
+     * </p>
+     *
+     * @return the number of segments
+     */
+    public int segmentCount();
+
+    /**
+     * Returns the segments in this path in order.
+     *
+     * @return an array of string segments
+     */
+    public String[] segments();
+
+    /**
+     * Returns a new path which is the same as this path but with
+     * the given device id.  The device id must end with a ":".
+     * A device independent path is obtained by passing <code>null</code>.
+     * <p>
+     * For example, "C:" and "Server/Volume:" are typical device ids.
+     * </p>
+     *
+     * @param device the device id or <code>null</code>
+     * @return a new path
+     * @see #getDevice()
+     */
+    public IPath setDevice(String device);
+
+    /**
+     * Returns a <code>java.io.File</code> corresponding to this path.
+     *
+     * @return the file corresponding to this path
+     */
+    public tango.io.FilePath.FilePath toFile();
+
+    /**
+     * Returns a string representation of this path which uses the
+     * platform-dependent path separator defined by <code>java.io.File</code>.
+     * This method is like <code>toString()</code> except that the
+     * latter always uses the same separator (<code>/</code>) regardless of platform.
+     * <p>
+     * This string is suitable for passing to <code>java.io.File(String)</code>.
+     * </p>
+     *
+     * @return a platform-dependent string representation of this path
+     */
+    public String toOSString();
+
+    /**
+     * Returns a platform-neutral string representation of this path. The
+     * format is not specified, except that the resulting string can be
+     * passed back to the <code>Path#fromPortableString(String)</code>
+     * constructor to produce the exact same path on any platform.
+     * <p>
+     * This string is suitable for passing to <code>Path#fromPortableString(String)</code>.
+     * </p>
+     *
+     * @return a platform-neutral string representation of this path
+     * @see Path#fromPortableString(String)
+     * @since 3.1
+     */
+    public String toPortableString();
+
+    /**
+     * Returns a string representation of this path, including its
+     * device id.  The same separator, "/", is used on all platforms.
+     * <p>
+     * Example result strings (without and with device id):
+     * <pre>
+     * "/foo/bar.txt"
+     * "bar.txt"
+     * "/foo/"
+     * "foo/"
+     * ""
+     * "/"
+     * "C:/foo/bar.txt"
+     * "C:bar.txt"
+     * "C:/foo/"
+     * "C:foo/"
+     * "C:"
+     * "C:/"
+     * </pre>
+     * This string is suitable for passing to <code>Path(String)</code>.
+     * </p>
+     *
+     * @return a string representation of this path
+     * @see Path
+     */
+    public String toString();
+
+    /**
+     * Returns a copy of this path truncated after the
+     * given number of segments. The number must not be negative.
+     * The device id is preserved.
+     * <p>
+     * If this path has a trailing separator, the result will too
+     * (assuming there are some segments left). If there is no
+     * trailing separator, the result will not have a trailing
+     * separator.
+     * Copying up to segment zero simply means making an copy with
+     * no path segments.
+     * </p>
+     *
+     * @param count the segment number at which to truncate the path
+     * @return the new path
+     */
+    public IPath uptoSegment(int count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IProgressMonitor.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IProgressMonitor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The <code>IProgressMonitor</code> interface is implemented
+ * by objects that monitor the progress of an activity; the methods
+ * in this interface are invoked by code that performs the activity.
+ * <p>
+ * All activity is broken down into a linear sequence of tasks against
+ * which progress is reported. When a task begins, a <code>beginTask(String, int)
+ * </code> notification is reported, followed by any number and mixture of
+ * progress reports (<code>worked()</code>) and subtask notifications
+ * (<code>subTask(String)</code>).  When the task is eventually completed, a
+ * <code>done()</code> notification is reported.  After the <code>done()</code>
+ * notification, the progress monitor cannot be reused;  i.e., <code>
+ * beginTask(String, int)</code> cannot be called again after the call to
+ * <code>done()</code>.
+ * </p>
+ * <p>
+ * A request to cancel an operation can be signaled using the
+ * <code>setCanceled</code> method.  Operations taking a progress
+ * monitor are expected to poll the monitor (using <code>isCanceled</code>)
+ * periodically and abort at their earliest convenience.  Operation can however
+ * choose to ignore cancelation requests.
+ * </p>
+ * <p>
+ * Since notification is synchronous with the activity itself, the listener should
+ * provide a fast and robust implementation. If the handling of notifications would
+ * involve blocking operations, or operations which might throw uncaught exceptions,
+ * the notifications should be queued, and the actual processing deferred (or perhaps
+ * delegated to a separate thread).
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ */
+public interface IProgressMonitor {
+
+    /** Constant indicating an unknown amount of work.
+     */
+    public final const int UNKNOWN = -1;
+
+    /**
+     * Notifies that the main task is beginning.  This must only be called once
+     * on a given progress monitor instance.
+     *
+     * @param name the name (or description) of the main task
+     * @param totalWork the total number of work units into which
+     *  the main task is been subdivided. If the value is <code>UNKNOWN</code>
+     *  the implementation is free to indicate progress in a way which
+     *  doesn't require the total number of work units in advance.
+     */
+    public void beginTask(String name, int totalWork);
+
+    /**
+     * Notifies that the work is done; that is, either the main task is completed
+     * or the user canceled it. This method may be called more than once
+     * (implementations should be prepared to handle this case).
+     */
+    public void done();
+
+    /**
+     * Internal method to handle scaling correctly. This method
+     * must not be called by a client. Clients should
+     * always use the method </code>worked(int)</code>.
+     *
+     * @param work the amount of work done
+     */
+    public void internalWorked(double work);
+
+    /**
+     * Returns whether cancelation of current operation has been requested.
+     * Long-running operations should poll to see if cancelation
+     * has been requested.
+     *
+     * @return <code>true</code> if cancellation has been requested,
+     *    and <code>false</code> otherwise
+     * @see #setCanceled(bool)
+     */
+    public bool isCanceled();
+
+    /**
+     * Sets the cancel state to the given value.
+     *
+     * @param value <code>true</code> indicates that cancelation has
+     *     been requested (but not necessarily acknowledged);
+     *     <code>false</code> clears this flag
+     * @see #isCanceled()
+     */
+    public void setCanceled(bool value);
+
+    /**
+     * Sets the task name to the given value. This method is used to
+     * restore the task label after a nested operation was executed.
+     * Normally there is no need for clients to call this method.
+     *
+     * @param name the name (or description) of the main task
+     * @see #beginTask(java.lang.String, int)
+     */
+    public void setTaskName(String name);
+
+    /**
+     * Notifies that a subtask of the main task is beginning.
+     * Subtasks are optional; the main task might not have subtasks.
+     *
+     * @param name the name (or description) of the subtask
+     */
+    public void subTask(String name);
+
+    /**
+     * Notifies that a given number of work unit of the main task
+     * has been completed. Note that this amount represents an
+     * installment, as opposed to a cumulative amount of work done
+     * to date.
+     *
+     * @param work a non-negative number of work units just completed
+     */
+    public void worked(int work);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IProgressMonitorWithBlocking.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 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 - Initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IProgressMonitorWithBlocking;
+
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An extension to the IProgressMonitor interface for monitors that want to
+ * support feedback when an activity is blocked due to concurrent activity in
+ * another thread.
+ * <p>
+ * When a monitor that supports this extension is passed to an operation, the
+ * operation should call <code>setBlocked</code> whenever it knows that it
+ * must wait for a lock that is currently held by another thread. The operation
+ * should continue to check for and respond to cancelation requests while
+ * blocked. When the operation is no longer blocked, it must call <code>clearBlocked</code>
+ * to clear the blocked state.
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see IProgressMonitor
+ * @since 3.0
+ */
+public interface IProgressMonitorWithBlocking : IProgressMonitor {
+    /**
+     * Indicates that this operation is blocked by some background activity. If
+     * a running operation ever calls <code>setBlocked</code>, it must
+     * eventually call <code>clearBlocked</code> before the operation
+     * completes.
+     * <p>
+     * If the caller is blocked by a currently executing job, this method will return
+     * an <code>IJobStatus</code> indicating the job that is currently blocking
+     * the caller. If this blocking job is not known, this method will return a plain
+     * informational <code>IStatus</code> object.
+     * </p>
+     *
+     * @param reason an optional status object whose message describes the
+     * reason why this operation is blocked, or <code>null</code> if this
+     * information is not available.
+     * @see #clearBlocked()
+     * @see dwtx.core.runtime.jobs.IJobStatus
+     */
+    public void setBlocked(IStatus reason);
+
+    /**
+     * Clears the blocked state of the running operation. If a running
+     * operation ever calls <code>setBlocked</code>, it must eventually call
+     * <code>clearBlocked</code> before the operation completes.
+     *
+     * @see #setBlocked(IStatus)
+     */
+    public void clearBlocked();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/ISafeRunnable.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.ISafeRunnable;
+
+// import dwt.internal.Platform;
+import dwt.dwthelper.utils;
+
+/**
+ * Safe runnables represent blocks of code and associated exception
+ * handlers.  They are typically used when a plug-in needs to call some
+ * untrusted code (e.g., code contributed by another plug-in via an
+ * extension).
+ * <p>
+ * This interface can be used without OSGi running.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see Platform#run(ISafeRunnable)
+ */
+public interface ISafeRunnable {
+    /**
+     * Handles an exception thrown by this runnable's <code>run</code>
+     * method.  The processing done here should be specific to the
+     * particular usecase for this runnable.  Generalized exception
+     * processing (e.g., logging in the platform's log) is done by the
+     * Platform's run mechanism.
+     *
+     * @param exception an exception which occurred during processing
+     *      the body of this runnable (i.e., in <code>run()</code>)
+     * @see Platform#run(ISafeRunnable)
+     */
+    public void handleException(Exception exception);
+
+    /**
+     * Runs this runnable.  Any exceptions thrown from this method will
+     * be passed to this runnable's <code>handleException</code>
+     * method.
+     *
+     * @exception Exception if a problem occurred while running this method.
+     *      The exception will be processed by <code>handleException</code>
+     * @see Platform#run(ISafeRunnable)
+     */
+    public void run();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/IStatus.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.IStatus;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A status object represents the outcome of an operation.
+ * All <code>CoreException</code>s carry a status object to indicate
+ * what went wrong. Status objects are also returned by methods needing
+ * to provide details of failures (e.g., validation methods).
+ * <p>
+ * A status carries the following information:
+ * <ul>
+ * <li> plug-in identifier (required)</li>
+ * <li> severity (required)</li>
+ * <li> status code (required)</li>
+ * <li> message (required) - localized to current locale</li>
+ * <li> exception (optional) - for problems stemming from a failure at
+ *    a lower level</li>
+ * </ul>
+ * Some status objects, known as multi-statuses, have other status objects
+ * as children.
+ * </p>
+ * <p>
+ * The class <code>Status</code> is the standard public implementation
+ * of status objects; the subclass <code>MultiStatus</code> is the
+ * implements multi-status objects.
+ * </p><p>
+ * This interface can be used without OSGi running.
+ * </p>
+ * @see MultiStatus
+ * @see Status
+ */
+public interface IStatus {
+
+    /** Status severity constant (value 0) indicating this status represents the nominal case.
+     * This constant is also used as the status code representing the nominal case.
+     * @see #getSeverity()
+     * @see #isOK()
+     */
+    public static const int OK = 0;
+
+    /** Status type severity (bit mask, value 1) indicating this status is informational only.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int INFO = 0x01;
+
+    /** Status type severity (bit mask, value 2) indicating this status represents a warning.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int WARNING = 0x02;
+
+    /** Status type severity (bit mask, value 4) indicating this status represents an error.
+     * @see #getSeverity()
+     * @see #matches(int)
+     */
+    public static const int ERROR = 0x04;
+
+    /** Status type severity (bit mask, value 8) indicating this status represents a
+     * cancelation
+     * @see #getSeverity()
+     * @see #matches(int)
+     * @since 3.0
+     */
+    public static const int CANCEL = 0x08;
+
+    /**
+     * Returns a list of status object immediately contained in this
+     * multi-status, or an empty list if this is not a multi-status.
+     *
+     * @return an array of status objects
+     * @see #isMultiStatus()
+     */
+    public IStatus[] getChildren();
+
+    /**
+     * Returns the plug-in-specific status code describing the outcome.
+     *
+     * @return plug-in-specific status code
+     */
+    public int getCode();
+
+    /**
+     * Returns the relevant low-level exception, or <code>null</code> if none.
+     * For example, when an operation fails because of a network communications
+     * failure, this might return the <code>java.io.IOException</code>
+     * describing the exact nature of that failure.
+     *
+     * @return the relevant low-level exception, or <code>null</code> if none
+     */
+    public Exception getException();
+
+    /**
+     * Returns the message describing the outcome.
+     * The message is localized to the current locale.
+     *
+     * @return a localized message
+     */
+    public String getMessage();
+
+    /**
+     * Returns the unique identifier of the plug-in associated with this status
+     * (this is the plug-in that defines the meaning of the status code).
+     *
+     * @return the unique identifier of the relevant plug-in
+     */
+    public String getPlugin();
+
+    /**
+     * Returns the severity. The severities are as follows (in
+     * descending order):
+     * <ul>
+     * <li><code>CANCEL</code> - cancelation occurred</li>
+     * <li><code>ERROR</code> - a serious error (most severe)</li>
+     * <li><code>WARNING</code> - a warning (less severe)</li>
+     * <li><code>INFO</code> - an informational ("fyi") message (least severe)</li>
+     * <li><code>OK</code> - everything is just fine</li>
+     * </ul>
+     * <p>
+     * The severity of a multi-status is defined to be the maximum
+     * severity of any of its children, or <code>OK</code> if it has
+     * no children.
+     * </p>
+     *
+     * @return the severity: one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @see #matches(int)
+     */
+    public int getSeverity();
+
+    /**
+     * Returns whether this status is a multi-status.
+     * A multi-status describes the outcome of an operation
+     * involving multiple operands.
+     * <p>
+     * The severity of a multi-status is derived from the severities
+     * of its children; a multi-status with no children is
+     * <code>OK</code> by definition.
+     * A multi-status carries a plug-in identifier, a status code,
+     * a message, and an optional exception. Clients may treat
+     * multi-status objects in a multi-status unaware way.
+     * </p>
+     *
+     * @return <code>true</code> for a multi-status,
+     *    <code>false</code> otherwise
+     * @see #getChildren()
+     */
+    public bool isMultiStatus();
+
+    /**
+     * Returns whether this status indicates everything is okay
+     * (neither info, warning, nor error).
+     *
+     * @return <code>true</code> if this status has severity
+     *    <code>OK</code>, and <code>false</code> otherwise
+     */
+    public bool isOK();
+
+    /**
+     * Returns whether the severity of this status matches the given
+     * severity mask. Note that a status with severity <code>OK</code>
+     * will never match; use <code>isOK</code> instead to detect
+     * a status with a severity of <code>OK</code>.
+     *
+     * @param severityMask a mask formed by bitwise or'ing severity mask
+     *    constants (<code>ERROR</code>, <code>WARNING</code>,
+     *    <code>INFO</code>, <code>CANCEL</code>)
+     * @return <code>true</code> if there is at least one match,
+     *    <code>false</code> if there are no matches
+     * @see #getSeverity()
+     * @see #CANCEL
+     * @see #ERROR
+     * @see #WARNING
+     * @see #INFO
+     */
+    public bool matches(int severityMask);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/ListenerList.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.ListenerList;
+
+import dwt.dwthelper.utils;
+
+/**
+ * This class is a thread safe list that is designed for storing lists of listeners.
+ * The implementation is optimized for minimal memory footprint, frequent reads
+ * and infrequent writes.  Modification of the list is synchronized and relatively
+ * expensive, while accessing the listeners is very fast.  Readers are given access
+ * to the underlying array data structure for reading, with the trust that they will
+ * not modify the underlying array.
+ * <p>
+ * <a name="same">A listener list handles the <i>same</i> listener being added
+ * multiple times, and tolerates removal of listeners that are the same as other
+ * listeners in the list.  For this purpose, listeners can be compared with each other
+ * using either equality or identity, as specified in the list constructor.
+ * </p>
+ * <p>
+ * Use the <code>getListeners</code> method when notifying listeners. The recommended
+ * code sequence for notifying all registered listeners of say,
+ * <code>FooListener.eventHappened</code>, is:
+ *
+ * <pre>
+ * Object[] listeners = myListenerList.getListeners();
+ * for (int i = 0; i &lt; listeners.length; ++i) {
+ *  ((FooListener) listeners[i]).eventHappened(event);
+ * }
+ * </pre>
+ *
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @since dwtx.equinox.common 3.2
+ */
+public class ListenerList {
+
+    /**
+     * The empty array singleton instance.
+     */
+    private static const Object[] EmptyArray;
+
+    /**
+     * Mode constant (value 0) indicating that listeners should be considered
+     * the <a href="#same">same</a> if they are equal.
+     */
+    public static const int EQUALITY = 0;
+
+    /**
+     * Mode constant (value 1) indicating that listeners should be considered
+     * the <a href="#same">same</a> if they are identical.
+     */
+    public static const int IDENTITY = 1;
+
+    /**
+     * Indicates the comparison mode used to determine if two
+     * listeners are equivalent
+     */
+    private final bool identity;
+
+    /**
+     * The list of listeners.  Initially empty but initialized
+     * to an array of size capacity the first time a listener is added.
+     * Maintains invariant: listeners !is null
+     */
+    private Object[] listeners;
+
+    /**
+     * Creates a listener list in which listeners are compared using equality.
+     */
+    public this() {
+        this(EQUALITY);
+    }
+
+    /**
+     * Creates a listener list using the provided comparison mode.
+     *
+     * @param mode The mode used to determine if listeners are the <a href="#same">same</a>.
+     */
+    public this(int mode) {
+        if (mode !is EQUALITY && mode !is IDENTITY)
+            throw new IllegalArgumentException( null );
+        this.identity = mode is IDENTITY;
+    }
+
+    /**
+     * Adds a listener to this list. This method has no effect if the <a href="#same">same</a>
+     * listener is already registered.
+     *
+     * @param listener the non-<code>null</code> listener to add
+     */
+    public synchronized void add(Object listener) {
+        // This method is synchronized to protect against multiple threads adding
+        // or removing listeners concurrently. This does not block concurrent readers.
+        if (listener is null)
+            throw new IllegalArgumentException( null );
+        // check for duplicates
+        final int oldSize = listeners.length;
+        for (int i = 0; i < oldSize; ++i) {
+            Object listener2 = listeners[i];
+            if (identity ? listener is listener2 : listener.opEquals(listener2))
+                return;
+        }
+        // Thread safety: create new array to avoid affecting concurrent readers
+        Object[] newListeners = new Object[oldSize + 1];
+        System.arraycopy(listeners, 0, newListeners, 0, oldSize);
+        newListeners[oldSize] = listener;
+        //atomic assignment
+        this.listeners = newListeners;
+    }
+
+    /**
+     * Returns an array containing all the registered listeners.
+     * The resulting array is unaffected by subsequent adds or removes.
+     * If there are no listeners registered, the result is an empty array.
+     * Use this method when notifying listeners, so that any modifications
+     * to the listener list during the notification will have no effect on
+     * the notification itself.
+     * <p>
+     * Note: Callers of this method <b>must not</b> modify the returned array.
+     *
+     * @return the list of registered listeners
+     */
+    public Object[] getListeners() {
+        return listeners;
+    }
+
+    /**
+     * Returns whether this listener list is empty.
+     *
+     * @return <code>true</code> if there are no registered listeners, and
+     *   <code>false</code> otherwise
+     */
+    public bool isEmpty() {
+        return listeners.length is 0;
+    }
+
+    /**
+     * Removes a listener from this list. Has no effect if the <a href="#same">same</a>
+     * listener was not already registered.
+     *
+     * @param listener the non-<code>null</code> listener to remove
+     */
+    public synchronized void remove(Object listener) {
+        // This method is synchronized to protect against multiple threads adding
+        // or removing listeners concurrently. This does not block concurrent readers.
+        if (listener is null)
+            throw new IllegalArgumentException( null );
+        int oldSize = listeners.length;
+        for (int i = 0; i < oldSize; ++i) {
+            Object listener2 = listeners[i];
+            if (identity ? listener is listener2 : listener.opEquals(listener2)) {
+                if (oldSize is 1) {
+                    listeners = EmptyArray;
+                } else {
+                    // Thread safety: create new array to avoid affecting concurrent readers
+                    Object[] newListeners = new Object[oldSize - 1];
+                    System.arraycopy(listeners, 0, newListeners, 0, i);
+                    System.arraycopy(listeners, i + 1, newListeners, i, oldSize - i - 1);
+                    //atomic assignment to field
+                    this.listeners = newListeners;
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the number of registered listeners.
+     *
+     * @return the number of registered listeners
+     */
+    public int size() {
+        return listeners.length;
+    }
+
+    /**
+     * Removes all listeners from this list.
+     */
+    public synchronized void clear() {
+            listeners = EmptyArray;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/MultiStatus.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.MultiStatus;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.IStatus;
+
+import dwt.dwthelper.utils;
+static import tango.text.Text;
+
+/**
+ * A concrete multi-status implementation,
+ * suitable either for instantiating or subclassing.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class MultiStatus : Status {
+
+    /** List of child statuses.
+     */
+    private IStatus[] children;
+
+    /**
+     * Creates and returns a new multi-status object with the given children.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code
+     * @param newChildren the list of children status objects
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(String pluginId, int code, IStatus[] newChildren, String message, Exception exception) {
+        this(pluginId, code, message, exception);
+        Assert.isLegal(newChildren !is null);
+        int maxSeverity = getSeverity();
+        for (int i = 0; i < newChildren.length; i++) {
+            Assert.isLegal(newChildren[i] !is null);
+            int severity = newChildren[i].getSeverity();
+            if (severity > maxSeverity)
+                maxSeverity = severity;
+        }
+        this.children = new IStatus[newChildren.length];
+        setSeverity(maxSeverity);
+        SimpleType!(IStatus).arraycopy(newChildren, 0, this.children, 0, newChildren.length);
+    }
+
+    /**
+     * Creates and returns a new multi-status object with no children.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(String pluginId, int code, String message, Exception exception) {
+        super(OK, pluginId, code, message, exception);
+        children = new IStatus[0];
+    }
+
+    /**
+     * Adds the given status to this multi-status.
+     *
+     * @param status the new child status
+     */
+    public void add(IStatus status) {
+        Assert.isLegal(status !is null);
+        IStatus[] result = new IStatus[children.length + 1];
+        SimpleType!(IStatus).arraycopy(children, 0, result, 0, children.length);
+        result[result.length - 1] = status;
+        children = result;
+        int newSev = status.getSeverity();
+        if (newSev > getSeverity()) {
+            setSeverity(newSev);
+        }
+    }
+
+    /**
+     * Adds all of the children of the given status to this multi-status.
+     * Does nothing if the given status has no children (which includes
+     * the case where it is not a multi-status).
+     *
+     * @param status the status whose children are to be added to this one
+     */
+    public void addAll(IStatus status) {
+        Assert.isLegal(status !is null);
+        IStatus[] statuses = status.getChildren();
+        for (int i = 0; i < statuses.length; i++) {
+            add(statuses[i]);
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public IStatus[] getChildren() {
+        return children;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool isMultiStatus() {
+        return true;
+    }
+
+    /**
+     * Merges the given status into this multi-status.
+     * Equivalent to <code>add(status)</code> if the
+     * given status is not a multi-status.
+     * Equivalent to <code>addAll(status)</code> if the
+     * given status is a multi-status.
+     *
+     * @param status the status to merge into this one
+     * @see #add(IStatus)
+     * @see #addAll(IStatus)
+     */
+    public void merge(IStatus status) {
+        Assert.isLegal(status !is null);
+        if (!status.isMultiStatus()) {
+            add(status);
+        } else {
+            addAll(status);
+        }
+    }
+
+    /**
+     * Returns a string representation of the status, suitable
+     * for debugging purposes only.
+     */
+    public String toString() {
+        tango.text.Text.Text!(char) buf = new tango.text.Text.Text!(char);
+        buf.append(super.toString());
+        buf.append(" children=["); //$NON-NLS-1$
+        for (int i = 0; i < children.length; i++) {
+            if (i !is 0) {
+                buf.append(" "); //$NON-NLS-1$
+            }
+            buf.append( (cast(Object)children[i]).toString());
+        }
+        buf.append("]"); //$NON-NLS-1$
+        return buf.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/NullProgressMonitor.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.NullProgressMonitor;
+
+import dwtx.core.runtime.IProgressMonitor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A default progress monitor implementation suitable for
+ * subclassing.
+ * <p>
+ * This implementation supports cancelation. The default
+ * implementations of the other methods do nothing.
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class NullProgressMonitor : IProgressMonitor {
+
+    /**
+     * Indicates whether cancel has been requested.
+     */
+    private bool cancelled = false;
+
+    /**
+     * Constructs a new progress monitor.
+     */
+    public this() {
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a task begins.
+     *
+     * @see IProgressMonitor#beginTask(String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a task is done.
+     *
+     * @see IProgressMonitor#done()
+     */
+    public void done() {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method.
+     *
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        // do nothing
+    }
+
+    /**
+     * This implementation returns the value of the internal
+     * state variable set by <code>setCanceled</code>.
+     * Subclasses which override this method should
+     * override <code>setCanceled</code> as well.
+     *
+     * @see IProgressMonitor#isCanceled()
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public bool isCanceled() {
+        return cancelled;
+    }
+
+    /**
+     * This implementation sets the value of an internal state variable.
+     * Subclasses which override this method should override
+     * <code>isCanceled</code> as well.
+     *
+     * @see IProgressMonitor#isCanceled()
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool cancelled) {
+        this.cancelled = cancelled;
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do something
+     * with the name of the task.
+     *
+     * @see IProgressMonitor#setTaskName(String)
+     */
+    public void setTaskName(String name) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when a subtask begins.
+     *
+     * @see IProgressMonitor#subTask(String)
+     */
+    public void subTask(String name) {
+        // do nothing
+    }
+
+    /**
+     * This implementation does nothing.
+     * Subclasses may override this method to do interesting
+     * processing when some work has been completed.
+     *
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        // do nothing
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/OperationCanceledException.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.OperationCanceledException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * This exception is thrown to blow out of a long-running method
+ * when the user cancels it.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients but
+ * may be instantiated.
+ * </p>
+ */
+public final class OperationCanceledException : RuntimeException {
+    /**
+     * All serializable objects should have a stable serialVersionUID
+     */
+    private static const long serialVersionUID = 1L;
+
+    /**
+     * Creates a new exception.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Creates a new exception with the given message.
+     *
+     * @param message the message for the exception
+     */
+    public this(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/Path.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,1006 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.Path;
+
+import tango.io.FilePath;
+import dwtx.core.runtime.IPath;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+import tango.io.FileConst;
+
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * The standard implementation of the <code>IPath</code> interface.
+ * Paths are always maintained in canonicalized form.  That is, parent
+ * references (i.e., <code>../../</code>) and duplicate separators are
+ * resolved.  For example,
+ * <pre>     new Path("/a/b").append("../foo/bar")</pre>
+ * will yield the path
+ * <pre>     /a/foo/bar</pre>
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients but
+ * may be instantiated.
+ * </p>
+ * @see IPath
+ */
+public class Path : IPath, Cloneable {
+    /** masks for separator values */
+    private static const int HAS_LEADING = 1;
+    private static const int IS_UNC = 2;
+    private static const int HAS_TRAILING = 4;
+
+    private static const int ALL_SEPARATORS = HAS_LEADING | IS_UNC | HAS_TRAILING;
+
+    /** Constant empty string value. */
+    private static const String EMPTY_STRING = ""; //$NON-NLS-1$
+
+    /** Constant value indicating no segments */
+    private static const String[] NO_SEGMENTS = null;
+
+    /** Constant value containing the empty path with no device. */
+    public static const Path EMPTY;
+
+    /** Mask for all bits that are involved in the hash code */
+    private static const int HASH_MASK = ~HAS_TRAILING;
+
+
+    /** Constant root path string (<code>"/"</code>). */
+    private static const String ROOT_STRING = "/"; //$NON-NLS-1$
+
+    /** Constant value containing the root path with no device. */
+    public static const Path ROOT;
+
+    /** Constant value indicating if the current platform is Windows */
+    version(Windows){
+        private static const bool WINDOWS = true;
+    }
+    else {
+        private static const bool WINDOWS = false;
+    }
+
+    static this(){
+        EMPTY = new Path(EMPTY_STRING);
+        ROOT = new Path(ROOT_STRING);
+    }
+
+    /** The device id string. May be null if there is no device. */
+    private String device = null;
+
+    //Private implementation note: the segments and separators
+    //arrays are never modified, so that they can be shared between
+    //path instances
+
+    /** The path segments */
+    private String[] segments_;
+
+    /** flags indicating separators (has leading, is UNC, has trailing) */
+    private int separators;
+
+    /**
+     * Constructs a new path from the given string path.
+     * The string path must represent a valid file system path
+     * on the local file system.
+     * The path is canonicalized and double slashes are removed
+     * except at the beginning. (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment and device delimiters for the local file system are
+     * also respected.
+     *
+     * @param pathString the portable string path
+     * @see IPath#toPortableString()
+     * @since 3.1
+     */
+    public static IPath fromOSString(String pathString) {
+        return new Path(pathString);
+    }
+
+    /**
+     * Constructs a new path from the given path string.
+     * The path string must have been produced by a previous
+     * call to <code>IPath.toPortableString</code>.
+     *
+     * @param pathString the portable path string
+     * @see IPath#toPortableString()
+     * @since 3.1
+     */
+    public static IPath fromPortableString(String pathString) {
+        int firstMatch = pathString.indexOf(DEVICE_SEPARATOR) +1;
+        //no extra work required if no device characters
+        if (firstMatch <= 0)
+            return (new Path()).initialize(null, pathString);
+        //if we find a single colon, then the path has a device
+        String devicePart = null;
+        int pathLength = pathString.length;
+        if (firstMatch is pathLength || pathString.charAt(firstMatch) !is DEVICE_SEPARATOR) {
+            devicePart = pathString.substring(0, firstMatch);
+            pathString = pathString.substring(firstMatch, pathLength);
+        }
+        //optimize for no colon literals
+        if (pathString.indexOf(DEVICE_SEPARATOR) is -1)
+            return (new Path()).initialize(devicePart, pathString);
+        //contract colon literals
+        char[] chars = pathString/+.toCharArray()+/;
+        int readOffset = 0, writeOffset = 0, length = chars.length;
+        while (readOffset < length) {
+            if (chars[readOffset] is DEVICE_SEPARATOR)
+                if (++readOffset >= length)
+                    break;
+            chars[writeOffset++] = chars[readOffset++];
+        }
+        return (new Path()).initialize(devicePart, chars[ 0 .. writeOffset] );
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Private constructor.
+     */
+    private this() {
+        // not allowed
+    }
+
+    /**
+     * Constructs a new path from the given string path.
+     * The string path must represent a valid file system path
+     * on the local file system.
+     * The path is canonicalized and double slashes are removed
+     * except at the beginning. (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment and device delimiters for the local file system are
+     * also respected (such as colon (':') and backslash ('\') on some file systems).
+     *
+     * @param fullPath the string path
+     * @see #isValidPath(String)
+     */
+    public this(String fullPath) {
+        String devicePart = null;
+        if (WINDOWS) {
+            //convert backslash to forward slash
+            fullPath = fullPath.indexOf('\\') is -1 ? fullPath : fullPath.replace('\\', SEPARATOR);
+            //extract device
+            int i = fullPath.indexOf(DEVICE_SEPARATOR);
+            if (i !is -1) {
+                //remove leading slash from device part to handle output of URL.getFile()
+                int start = fullPath.charAt(0) is SEPARATOR ? 1 : 0;
+                devicePart = fullPath.substring(start, i + 1);
+                fullPath = fullPath.substring(i + 1, fullPath.length);
+            }
+        }
+        initialize(devicePart, fullPath);
+    }
+
+    /**
+     * Constructs a new path from the given device id and string path.
+     * The given string path must be valid.
+     * The path is canonicalized and double slashes are removed except
+     * at the beginning (to handle UNC paths). All forward
+     * slashes ('/') are treated as segment delimiters, and any
+     * segment delimiters for the local file system are
+     * also respected (such as backslash ('\') on some file systems).
+     *
+     * @param device the device id
+     * @param path the string path
+     * @see #isValidPath(String)
+     * @see #setDevice(String)
+     */
+    public this(String device, String path) {
+        if (WINDOWS) {
+            //convert backslash to forward slash
+            path = path.indexOf('\\') is -1 ? path : path.replace('\\', SEPARATOR);
+        }
+        initialize(device, path);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Private constructor.
+     */
+    private this(String device, String[] segments_, int _separators) {
+        // no segment validations are done for performance reasons
+        this.segments_ = segments_;
+        this.device = device;
+        //hash code is cached in all but the bottom three bits of the separators field
+        this.separators = (computeHashCode() << 3) | (_separators & ALL_SEPARATORS);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#addFileExtension
+     */
+    public IPath addFileExtension(String extension) {
+        if (isRoot() || isEmpty() || hasTrailingSeparator())
+            return this;
+        int len = segments_.length;
+        String[] newSegments = new String[len];
+        System.arraycopy(segments_, 0, newSegments, 0, len - 1);
+        newSegments[len - 1] = segments_[len - 1] ~ '.' ~ extension;
+        return new Path(device, newSegments, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#addTrailingSeparator
+     */
+    public IPath addTrailingSeparator() {
+        if (hasTrailingSeparator() || isRoot()) {
+            return this;
+        }
+        //XXX workaround, see 1GIGQ9V
+        if (isEmpty()) {
+            return new Path(device, segments_, HAS_LEADING);
+        }
+        return new Path(device, segments_, separators | HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#append(IPath)
+     */
+    public IPath append(IPath tail) {
+        //optimize some easy cases
+        if (tail is null || tail.segmentCount() is 0)
+            return this;
+        //these call chains look expensive, but in most cases they are no-ops
+        if (this.isEmpty())
+            return tail.setDevice(device).makeRelative().makeUNC(isUNC());
+        if (this.isRoot())
+            return tail.setDevice(device).makeAbsolute().makeUNC(isUNC());
+
+        //concatenate the two segment arrays
+        int myLen = segments_.length;
+        int tailLen = tail.segmentCount();
+        String[] newSegments = new String[myLen + tailLen];
+        System.arraycopy(segments_, 0, newSegments, 0, myLen);
+        for (int i = 0; i < tailLen; i++) {
+            newSegments[myLen + i] = tail.segment(i);
+        }
+        //use my leading separators and the tail's trailing separator
+        Path result = new Path(device, newSegments, (separators & (HAS_LEADING | IS_UNC)) | (tail.hasTrailingSeparator() ? HAS_TRAILING : 0));
+        String tailFirstSegment = newSegments[myLen];
+        if (tailFirstSegment.equals("..") || tailFirstSegment.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
+            result.canonicalize();
+        }
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#append(java.lang.String)
+     */
+    public IPath append(String tail) {
+        //optimize addition of a single segment
+        if (tail.indexOf(SEPARATOR) is -1 && tail.indexOf("\\") is -1 && tail.indexOf(DEVICE_SEPARATOR) is -1) { //$NON-NLS-1$
+            int tailLength = tail.length;
+            if (tailLength < 3) {
+                //some special cases
+                if (tailLength is 0 || ".".equals(tail)) { //$NON-NLS-1$
+                    return this;
+                }
+                if ("..".equals(tail)) //$NON-NLS-1$
+                    return removeLastSegments(1);
+            }
+            //just add the segment
+            int myLen = segments_.length;
+            String[] newSegments = new String[myLen + 1];
+            System.arraycopy(segments_, 0, newSegments, 0, myLen);
+            newSegments[myLen] = tail;
+            return new Path(device, newSegments, separators & ~HAS_TRAILING);
+        }
+        //go with easy implementation
+        return append(new Path(tail));
+    }
+
+    /**
+     * Destructively converts this path to its canonical form.
+     * <p>
+     * In its canonical form, a path does not have any
+     * "." segments, and parent references ("..") are collapsed
+     * where possible.
+     * </p>
+     * @return true if the path was modified, and false otherwise.
+     */
+    private bool canonicalize() {
+        //look for segments that need canonicalizing
+        for (int i = 0, max = segments_.length; i < max; i++) {
+            String segment = segments_[i];
+            if (segment.charAt(0) is '.' && (segment.equals("..") || segment.equals("."))) { //$NON-NLS-1$ //$NON-NLS-2$
+                //path needs to be canonicalized
+                collapseParentReferences();
+                //paths of length 0 have no trailing separator
+                if (segments_.length is 0)
+                    separators &= (HAS_LEADING | IS_UNC);
+                //recompute hash because canonicalize affects hash
+                separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Clones this object.
+     */
+    public Path clone() {
+        return new Path(device, segments_, separators);
+    }
+
+    /**
+     * Destructively removes all occurrences of ".." segments from this path.
+     */
+    private void collapseParentReferences() {
+        int segmentCount = segments_.length;
+        String[] stack = new String[segmentCount];
+        int stackPointer = 0;
+        for (int i = 0; i < segmentCount; i++) {
+            String segment = segments_[i];
+            if (segment.equals("..")) { //$NON-NLS-1$
+                if (stackPointer is 0) {
+                    // if the stack is empty we are going out of our scope
+                    // so we need to accumulate segments.  But only if the original
+                    // path is relative.  If it is absolute then we can't go any higher than
+                    // root so simply toss the .. references.
+                    if (!isAbsolute())
+                        stack[stackPointer++] = segment; //stack push
+                } else {
+                    // if the top is '..' then we are accumulating segments so don't pop
+                    if ("..".equals(stack[stackPointer - 1])) //$NON-NLS-1$
+                        stack[stackPointer++] = ".."; //$NON-NLS-1$
+                    else
+                        stackPointer--;
+                    //stack pop
+                }
+                //collapse current references
+            } else if (!segment.equals(".") || segmentCount is 1) //$NON-NLS-1$
+                stack[stackPointer++] = segment; //stack push
+        }
+        //if the number of segments hasn't changed, then no modification needed
+        if (stackPointer is segmentCount)
+            return;
+        //build the new segment array backwards by popping the stack
+        String[] newSegments = new String[stackPointer];
+        System.arraycopy(stack, 0, newSegments, 0, stackPointer);
+        this.segments_ = newSegments;
+    }
+
+    /**
+     * Removes duplicate slashes from the given path, with the exception
+     * of leading double slash which represents a UNC path.
+     */
+    private String collapseSlashes(String path) {
+        int length = path.length;
+        // if the path is only 0, 1 or 2 chars long then it could not possibly have illegal
+        // duplicate slashes.
+        if (length < 3)
+            return path;
+        // check for an occurrence of // in the path.  Start at index 1 to ensure we skip leading UNC //
+        // If there are no // then there is nothing to collapse so just return.
+        if (path.indexOf("//", 1) is -1) //$NON-NLS-1$
+            return path;
+        // We found an occurrence of // in the path so do the slow collapse.
+        char[] result = new char[path.length];
+        int count = 0;
+        bool hasPrevious = false;
+        char[] characters = path/+.toCharArray()+/;
+        for (int index = 0; index < characters.length; index++) {
+            char c = characters[index];
+            if (c is SEPARATOR) {
+                if (hasPrevious) {
+                    // skip double slashes, except for beginning of UNC.
+                    // note that a UNC path can't have a device.
+                    if (device is null && index is 1) {
+                        result[count] = c;
+                        count++;
+                    }
+                } else {
+                    hasPrevious = true;
+                    result[count] = c;
+                    count++;
+                }
+            } else {
+                hasPrevious = false;
+                result[count] = c;
+                count++;
+            }
+        }
+        return result[ 0 .. count];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Computes the hash code for this object.
+     */
+    private int computeHashCode() {
+        int hash = device.length is 0 ? 17 : dwt.dwthelper.utils.toHash(device);
+        int segmentCount = segments_.length;
+        for (int i = 0; i < segmentCount; i++) {
+            //this function tends to given a fairly even distribution
+            hash = hash * 37 + dwt.dwthelper.utils.toHash(segments_[i]);
+        }
+        return hash;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Returns the size of the string that will be created by toString or toOSString.
+     */
+    private int computeLength() {
+        int length = 0;
+        if (device !is null)
+            length += device.length;
+        if ((separators & HAS_LEADING) !is 0)
+            length++;
+        if ((separators & IS_UNC) !is 0)
+            length++;
+        //add the segment lengths
+        int max = segments_.length;
+        if (max > 0) {
+            for (int i = 0; i < max; i++) {
+                length += segments_[i].length;
+            }
+            //add the separator lengths
+            length += max - 1;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            length++;
+        return length;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Returns the number of segments in the given path
+     */
+    private int computeSegmentCount(String path) {
+        int len = path.length;
+        if (len is 0 || (len is 1 && path.charAt(0) is SEPARATOR)) {
+            return 0;
+        }
+        int count = 1;
+        int prev = -1;
+        int i;
+        while ((i = path.indexOf(SEPARATOR, prev + 1)) !is -1) {
+            if (i !is prev + 1 && i !is len) {
+                ++count;
+            }
+            prev = i;
+        }
+        if (path.charAt(len - 1) is SEPARATOR) {
+            --count;
+        }
+        return count;
+    }
+
+    /**
+     * Computes the segment array for the given canonicalized path.
+     */
+    private String[] computeSegments(String path) {
+        // performance sensitive --- avoid creating garbage
+        int segmentCount = computeSegmentCount(path);
+        if (segmentCount is 0)
+            return NO_SEGMENTS;
+        String[] newSegments = new String[segmentCount];
+        int len = path.length;
+        // check for initial slash
+        int firstPosition = (path.charAt(0) is SEPARATOR) ? 1 : 0;
+        // check for UNC
+        if (firstPosition is 1 && len > 1 && (path.charAt(1) is SEPARATOR))
+            firstPosition = 2;
+        int lastPosition = (path.charAt(len - 1) !is SEPARATOR) ? len - 1 : len - 2;
+        // for non-empty paths, the number of segments is
+        // the number of slashes plus 1, ignoring any leading
+        // and trailing slashes
+        int next = firstPosition;
+        for (int i = 0; i < segmentCount; i++) {
+            int start = next;
+            int end = path.indexOf(SEPARATOR, next);
+            if (end is -1) {
+                newSegments[i] = path.substring(start, lastPosition + 1);
+            } else {
+                newSegments[i] = path.substring(start, end);
+            }
+            next = end + 1;
+        }
+        return newSegments;
+    }
+    /**
+     * Returns the platform-neutral encoding of the given segment onto
+     * the given string buffer. This escapes literal colon characters with double colons.
+     */
+    private void encodeSegment(String string, StringBuffer buf) {
+        int len = string.length;
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            buf.append(c);
+            if (c is DEVICE_SEPARATOR)
+                buf.append(DEVICE_SEPARATOR);
+        }
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Compares objects for equality.
+     */
+    public override int opEquals(Object obj) {
+        if (this is obj)
+            return true;
+        if (!(cast(Path)obj))
+            return false;
+        Path target = cast(Path) obj;
+        //check leading separators and hash code
+        if ((separators & HASH_MASK) !is (target.separators & HASH_MASK))
+            return false;
+        String[] targetSegments = target.segments_;
+        int i = segments_.length;
+        //check segment count
+        if (i !is targetSegments.length)
+            return false;
+        //check segments in reverse order - later segments more likely to differ
+        while (--i >= 0)
+            if (!segments_[i].equals(targetSegments[i]))
+                return false;
+        //check device last (least likely to differ)
+        return device is target.device || (device !is null && device.equals(target.device));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#getDevice
+     */
+    public String getDevice() {
+        return device;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#getFileExtension
+     */
+    public String getFileExtension() {
+        if (hasTrailingSeparator()) {
+            return null;
+        }
+        String lastSegment = lastSegment();
+        if (lastSegment is null) {
+            return null;
+        }
+        int index = lastSegment.lastIndexOf('.');
+        if (index is -1) {
+            return null;
+        }
+        return lastSegment.substring(index + 1);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * Computes the hash code for this object.
+     */
+    public int hashCode() {
+        return separators & HASH_MASK;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#hasTrailingSeparator2
+     */
+    public bool hasTrailingSeparator() {
+        return (separators & HAS_TRAILING) !is 0;
+    }
+
+    /*
+     * Initialize the current path with the given string.
+     */
+    private IPath initialize(String deviceString, String path) {
+        Assert.isTrue(path.length > 0);
+        this.device = deviceString;
+
+        path = collapseSlashes(path);
+        int len = path.length;
+
+        //compute the separators array
+        if (len < 2) {
+            if (len is 1 && path.charAt(0) is SEPARATOR) {
+                this.separators = HAS_LEADING;
+            } else {
+                this.separators = 0;
+            }
+        } else {
+            bool hasLeading = path.charAt(0) is SEPARATOR;
+            bool isUNC = hasLeading && path.charAt(1) is SEPARATOR;
+            //UNC path of length two has no trailing separator
+            bool hasTrailing = !(isUNC && len is 2) && path.charAt(len - 1) is SEPARATOR;
+            separators = hasLeading ? HAS_LEADING : 0;
+            if (isUNC)
+                separators |= IS_UNC;
+            if (hasTrailing)
+                separators |= HAS_TRAILING;
+        }
+        //compute segments and ensure canonical form
+        segments_ = computeSegments(path);
+        if (!canonicalize()) {
+            //compute hash now because canonicalize didn't need to do it
+            separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
+        }
+        return this;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isAbsolute
+     */
+    public bool isAbsolute() {
+        //it's absolute if it has a leading separator
+        return (separators & HAS_LEADING) !is 0;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isEmpty
+     */
+    public bool isEmpty() {
+        //true if no segments and no leading prefix
+        return segments_.length is 0 && ((separators & ALL_SEPARATORS) !is HAS_LEADING);
+
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isPrefixOf
+     */
+    public bool isPrefixOf(IPath anotherPath) {
+        if (device is null) {
+            if (anotherPath.getDevice() !is null) {
+                return false;
+            }
+        } else {
+            if (!device.equalsIgnoreCase(anotherPath.getDevice())) {
+                return false;
+            }
+        }
+        if (isEmpty() || (isRoot() && anotherPath.isAbsolute())) {
+            return true;
+        }
+        int len = segments_.length;
+        if (len > anotherPath.segmentCount()) {
+            return false;
+        }
+        for (int i = 0; i < len; i++) {
+            if (!segments_[i].equals(anotherPath.segment(i)))
+                return false;
+        }
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isRoot
+     */
+    public bool isRoot() {
+        //must have no segments, a leading separator, and not be a UNC path.
+        return this is ROOT || (segments_.length is 0 && ((separators & ALL_SEPARATORS) is HAS_LEADING));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isUNC
+     */
+    public bool isUNC() {
+        if (device !is null)
+            return false;
+        return (separators & IS_UNC) !is 0;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isValidPath(String)
+     */
+    public bool isValidPath(String path) {
+        Path test = new Path(path);
+        for (int i = 0, max = test.segmentCount(); i < max; i++)
+            if (!isValidSegment(test.segment(i)))
+                return false;
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#isValidSegment(String)
+     */
+    public bool isValidSegment(String segment) {
+        int size = segment.length;
+        if (size is 0)
+            return false;
+        for (int i = 0; i < size; i++) {
+            char c = segment.charAt(i);
+            if (c is '/')
+                return false;
+            if (WINDOWS && (c is '\\' || c is ':'))
+                return false;
+        }
+        return true;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#lastSegment()
+     */
+    public String lastSegment() {
+        int len = segments_.length;
+        return len is 0 ? null : segments_[len - 1];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeAbsolute()
+     */
+    public IPath makeAbsolute() {
+        if (isAbsolute()) {
+            return this;
+        }
+        Path result = new Path(device, segments_, separators | HAS_LEADING);
+        //may need canonicalizing if it has leading ".." or "." segments
+        if (result.segmentCount() > 0) {
+            String first = result.segment(0);
+            if (first.equals("..") || first.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
+                result.canonicalize();
+            }
+        }
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeRelative()
+     */
+    public IPath makeRelative() {
+        if (!isAbsolute()) {
+            return this;
+        }
+        return new Path(device, segments_, separators & HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#makeUNC(bool)
+     */
+    public IPath makeUNC(bool toUNC) {
+        // if we are already in the right form then just return
+        if (!(toUNC ^ isUNC()))
+            return this;
+
+        int newSeparators = this.separators;
+        if (toUNC) {
+            newSeparators |= HAS_LEADING | IS_UNC;
+        } else {
+            //mask out the UNC bit
+            newSeparators &= HAS_LEADING | HAS_TRAILING;
+        }
+        return new Path(toUNC ? null : device, segments_, newSeparators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#matchingFirstSegments(IPath)
+     */
+    public int matchingFirstSegments(IPath anotherPath) {
+        Assert.isNotNull( cast(Object) anotherPath);
+        int anotherPathLen = anotherPath.segmentCount();
+        int max = Math.min(segments_.length, anotherPathLen);
+        int count = 0;
+        for (int i = 0; i < max; i++) {
+            if (!segments_[i].equals(anotherPath.segment(i))) {
+                return count;
+            }
+            count++;
+        }
+        return count;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeFileExtension()
+     */
+    public IPath removeFileExtension() {
+        String extension = getFileExtension();
+        if (extension is null || extension.equals("")) { //$NON-NLS-1$
+            return this;
+        }
+        String lastSegment = lastSegment();
+        int index = lastSegment.lastIndexOf(extension) - 1;
+        return removeLastSegments(1).append(lastSegment.substring(0, index));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeFirstSegments(int)
+     */
+    public IPath removeFirstSegments(int count) {
+        if (count is 0)
+            return this;
+        if (count >= segments_.length) {
+            return new Path(device, NO_SEGMENTS, 0);
+        }
+        Assert.isLegal(count > 0);
+        int newSize = segments_.length - count;
+        String[] newSegments = new String[newSize];
+        System.arraycopy(this.segments_, count, newSegments, 0, newSize);
+
+        //result is always a relative path
+        return new Path(device, newSegments, separators & HAS_TRAILING);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeLastSegments(int)
+     */
+    public IPath removeLastSegments(int count) {
+        if (count is 0)
+            return this;
+        if (count >= segments_.length) {
+            //result will have no trailing separator
+            return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
+        }
+        Assert.isLegal(count > 0);
+        int newSize = segments_.length - count;
+        String[] newSegments = new String[newSize];
+        System.arraycopy(this.segments_, 0, newSegments, 0, newSize);
+        return new Path(device, newSegments, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#removeTrailingSeparator()
+     */
+    public IPath removeTrailingSeparator() {
+        if (!hasTrailingSeparator()) {
+            return this;
+        }
+        return new Path(device, segments_, separators & (HAS_LEADING | IS_UNC));
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segment(int)
+     */
+    public String segment(int index) {
+        if (index >= segments_.length)
+            return null;
+        return segments_[index];
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segmentCount()
+     */
+    public int segmentCount() {
+        return segments_.length;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#segments()
+     */
+    public String[] segments() {
+        String[] segmentCopy = new String[](segments_.length);
+        System.arraycopy(segments_, 0, segmentCopy, 0, segments_.length);
+        return segmentCopy;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#setDevice(String)
+     */
+    public IPath setDevice(String value) {
+        if (value !is null) {
+            Assert.isTrue(value.indexOf(IPath.DEVICE_SEPARATOR) is (value.length - 1), "Last character should be the device separator"); //$NON-NLS-1$
+        }
+        //return the receiver if the device is the same
+        if (value is device || (value !is null && value.equals(device)))
+            return this;
+
+        return new Path(value, segments_, separators);
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toFile()
+     */
+    public FilePath toFile() {
+        return new FilePath(toOSString());
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toOSString()
+     */
+    public String toOSString() {
+        //Note that this method is identical to toString except
+        //it uses the OS file separator instead of the path separator
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        char FILE_SEPARATOR = FileConst.PathSeparatorChar;
+        char[] result = new char[resultSize];
+        int offset = 0;
+        if (device !is null) {
+            int size = device.length;
+            device.getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_LEADING) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        if ((separators & IS_UNC) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        int len = segments_.length - 1;
+        if (len >= 0) {
+            //append all but the last segment, with separators
+            for (int i = 0; i < len; i++) {
+                int size = segments_[i].length;
+                segments_[i].getChars(0, size, result, offset);
+                offset += size;
+                result[offset++] = FILE_SEPARATOR;
+            }
+            //append the last segment
+            int size = segments_[len].length;
+            segments_[len].getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            result[offset++] = FILE_SEPARATOR;
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toPortableString()
+     */
+    public String toPortableString() {
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        StringBuffer result = new StringBuffer(resultSize);
+        if (device !is null)
+            result.append(device);
+        if ((separators & HAS_LEADING) !is 0)
+            result.append(SEPARATOR);
+        if ((separators & IS_UNC) !is 0)
+            result.append(SEPARATOR);
+        int len = segments_.length;
+        //append all segments with separators
+        for (int i = 0; i < len; i++) {
+            if (segments_[i].indexOf(DEVICE_SEPARATOR) >= 0)
+                encodeSegment(segments_[i], result);
+            else
+                result.append(segments_[i]);
+            if (i < len-1 || (separators & HAS_TRAILING) !is 0)
+                result.append(SEPARATOR);
+        }
+        return result.toString();
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#toString()
+     */
+    public String toString() {
+        int resultSize = computeLength();
+        if (resultSize <= 0)
+            return EMPTY_STRING;
+        char[] result = new char[resultSize];
+        int offset = 0;
+        if (device !is null) {
+            int size = device.length;
+            device.getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_LEADING) !is 0)
+            result[offset++] = SEPARATOR;
+        if ((separators & IS_UNC) !is 0)
+            result[offset++] = SEPARATOR;
+        int len = segments_.length - 1;
+        if (len >= 0) {
+            //append all but the last segment, with separators
+            for (int i = 0; i < len; i++) {
+                int size = segments_[i].length;
+                segments_[i].getChars(0, size, result, offset);
+                offset += size;
+                result[offset++] = SEPARATOR;
+            }
+            //append the last segment
+            int size = segments_[len].length;
+            segments_[len].getChars(0, size, result, offset);
+            offset += size;
+        }
+        if ((separators & HAS_TRAILING) !is 0)
+            result[offset++] = SEPARATOR;
+        return result;
+    }
+
+    /* (Intentionally not included in javadoc)
+     * @see IPath#uptoSegment(int)
+     */
+    public IPath uptoSegment(int count) {
+        if (count is 0)
+            return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
+        if (count >= segments_.length)
+            return this;
+        Assert.isTrue(count > 0, "Invalid parameter to Path.uptoSegment"); //$NON-NLS-1$
+        String[] newSegments = new String[count];
+        System.arraycopy(segments_, 0, newSegments, 0, count);
+        return new Path(device, newSegments, separators);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/ProgressMonitorWrapper.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.ProgressMonitorWrapper;
+
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IProgressMonitorWithBlocking;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An abstract wrapper around a progress monitor which,
+ * unless overridden, forwards <code>IProgressMonitor</code>
+ * and <code>IProgressMonitorWithBlocking</code> methods to the wrapped progress monitor.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * Clients may subclass.
+ * </p>
+ */
+public abstract class ProgressMonitorWrapper : IProgressMonitor, IProgressMonitorWithBlocking {
+
+    /** The wrapped progress monitor. */
+    private IProgressMonitor progressMonitor;
+
+    /**
+     * Creates a new wrapper around the given monitor.
+     *
+     * @param monitor the progress monitor to forward to
+     */
+    protected this(IProgressMonitor monitor) {
+        Assert.isNotNull(cast(Object)monitor);
+        progressMonitor = monitor;
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#beginTask(String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        progressMonitor.beginTask(name, totalWork);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitorWithBlocking</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitorWithBlocking#clearBlocked()
+     * @since 3.0
+     */
+    public void clearBlocked() {
+        if ( auto mon = cast(IProgressMonitorWithBlocking)progressMonitor )
+            mon.clearBlocked();
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#done()
+     */
+    public void done() {
+        progressMonitor.done();
+    }
+
+    /**
+     * Returns the wrapped progress monitor.
+     *
+     * @return the wrapped progress monitor
+     */
+    public IProgressMonitor getWrappedProgressMonitor() {
+        return progressMonitor;
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        progressMonitor.internalWorked(work);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#isCanceled()
+     */
+    public bool isCanceled() {
+        return progressMonitor.isCanceled();
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitorWithBlocking</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitorWithBlocking#setBlocked(IStatus)
+     * @since 3.0
+     */
+    public void setBlocked(IStatus reason) {
+        if ( auto mon = cast(IProgressMonitorWithBlocking)progressMonitor)
+            mon.setBlocked(reason);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool b) {
+        progressMonitor.setCanceled(b);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#setTaskName(String)
+     */
+    public void setTaskName(String name) {
+        progressMonitor.setTaskName(name);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#subTask(String)
+     */
+    public void subTask(String name) {
+        progressMonitor.subTask(name);
+    }
+
+    /**
+     * This implementation of a <code>IProgressMonitor</code>
+     * method forwards to the wrapped progress monitor.
+     * Clients may override this method to do additional
+     * processing.
+     *
+     * @see IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        progressMonitor.worked(work);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/QualifiedName.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.QualifiedName;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Qualified names are two-part names: qualifier and local name.
+ * The qualifier must be in URI form (see RFC2396).
+ * Note however that the qualifier may be <code>null</code> if
+ * the default name space is being used.  The empty string is not
+ * a valid local name.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ */
+public final class QualifiedName {
+
+    /** Qualifier part (potentially <code>null</code>). */
+    /*package*/
+    String qualifier = null;
+
+    /** Local name part. */
+    /*package*/
+    String localName = null;
+
+    /**
+     * Creates and returns a new qualified name with the given qualifier
+     * and local name.  The local name must not be the empty string.
+     * The qualifier may be <code>null</code>.
+     * <p>
+     * Clients may instantiate.
+     * </p>
+     * @param qualifier the qualifier string, or <code>null</code>
+     * @param localName the local name string
+     */
+    public this(String qualifier, String localName) {
+        Assert.isLegal(localName !is null && localName.length !is 0);
+        this.qualifier = qualifier;
+        this.localName = localName;
+    }
+
+    /**
+     * Returns whether this qualified name is equivalent to the given object.
+     * <p>
+     * Qualified names are equal if and only if they have the same
+     * qualified parts and local parts.
+     * Qualified names are not equal to objects other than qualified names.
+     * </p>
+     *
+     * @param obj the object to compare to
+     * @return <code>true</code> if these are equivalent qualified
+     *    names, and <code>false</code> otherwise
+     */
+    public override int opEquals(Object obj) {
+        if (obj is this) {
+            return true;
+        }
+        if (!(cast(QualifiedName)obj )) {
+            return false;
+        }
+        QualifiedName qName = cast(QualifiedName) obj;
+        /* There may or may not be a qualifier */
+        if (qualifier is null && qName.getQualifier() !is null) {
+            return false;
+        }
+        if (qualifier !is null && !qualifier.equals(qName.getQualifier())) {
+            return false;
+        }
+        return localName.equals(qName.getLocalName());
+    }
+
+    /**
+     * Returns the local part of this name.
+     *
+     * @return the local name string
+     */
+    public String getLocalName() {
+        return localName;
+    }
+
+    /**
+     * Returns the qualifier part for this qualified name, or <code>null</code>
+     * if none.
+     *
+     * @return the qualifier string, or <code>null</code>
+     */
+    public String getQualifier() {
+        return qualifier;
+    }
+
+    /* (Intentionally omitted from javadoc)
+     * Implements the method <code>Object.hashCode</code>.
+     *
+     * Returns the hash code for this qualified name.
+     */
+    public override hash_t toHash() {
+        return (qualifier is null ? 0 : .toHash(qualifier)) + .toHash(localName);
+    }
+
+    /**
+     * Converts this qualified name into a string, suitable for
+     * debug purposes only.
+     */
+    public override String toString() {
+        return (getQualifier() is null ? "" : getQualifier() ~ ':') ~ getLocalName(); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/SafeRunner.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.SafeRunner;
+
+import dwtx.core.runtime.ISafeRunnable;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Runs the given ISafeRunnable in a protected mode: exceptions
+ * thrown in the runnable are logged and passed to the runnable's
+ * exception handler.  Such exceptions are not rethrown by this method.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ * @since dwtx.equinox.common 3.2
+ */
+public final class SafeRunner {
+
+    /**
+     * Runs the given runnable in a protected mode.   Exceptions
+     * thrown in the runnable are logged and passed to the runnable's
+     * exception handler.  Such exceptions are not rethrown by this method.
+     *
+     * @param code the runnable to run
+     */
+    public static void run(ISafeRunnable code) {
+        Assert.isNotNull(cast(Object)code);
+        try {
+            code.run();
+        } catch (Exception e) {
+            handleException(code, e);
+//FIXME
+//         } catch (LinkageError e) {
+//             handleException(code, e);
+        }
+    }
+
+    private static void handleException(ISafeRunnable code, Exception e) {
+//FIXME
+//      if (!(e instanceof OperationCanceledException)) {
+//          // try to obtain the correct plug-in id for the bundle providing the safe runnable
+//          Activator activator = Activator.getDefault();
+//          String pluginId = null;
+//          if (activator !is null)
+//              pluginId = activator.getBundleId(code);
+//          if (pluginId is null)
+//              pluginId = IRuntimeConstants.PI_COMMON;
+//          String message = NLS.bind(CommonMessages.meta_pluginProblems, pluginId);
+//          IStatus status;
+//          if (e instanceof CoreException) {
+//              status = new MultiStatus(pluginId, IRuntimeConstants.PLUGIN_ERROR, message, e);
+//              ((MultiStatus) status).merge(((CoreException) e).getStatus());
+//          } else {
+//              status = new Status(IStatus.ERROR, pluginId, IRuntimeConstants.PLUGIN_ERROR, message, e);
+//          }
+//          // Make sure user sees the exception: if the log is empty, log the exceptions on stderr
+//          if (!RuntimeLog.isEmpty())
+//              RuntimeLog.log(status);
+//          else
+//              e.printStackTrace();
+//      }
+        code.handleException(e);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/Status.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.Status;
+
+import dwtx.core.internal.runtime.IRuntimeConstants;
+import dwtx.core.internal.runtime.LocalizationUtils;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * A concrete status implementation, suitable either for
+ * instantiating or subclassing.
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ */
+public class Status : IStatus {
+
+    /**
+     * A standard OK status with an "ok"  message.
+     *
+     * @since 3.0
+     */
+    public static const IStatus OK_STATUS;
+    /**
+     * A standard CANCEL status with no message.
+     *
+     * @since 3.0
+     */
+    public static const IStatus CANCEL_STATUS;
+
+    static this(){
+        OK_STATUS = new Status(OK, IRuntimeConstants.PI_RUNTIME, OK, LocalizationUtils.safeLocalize("ok"), null); //$NON-NLS-1$
+        CANCEL_STATUS = new Status(CANCEL, IRuntimeConstants.PI_RUNTIME, 1, "", null); //$NON-NLS-1$
+    }
+
+    /**
+     * The severity. One of
+     * <ul>
+     * <li><code>CANCEL</code></li>
+     * <li><code>ERROR</code></li>
+     * <li><code>WARNING</code></li>
+     * <li><code>INFO</code></li>
+     * <li>or <code>OK</code> (0)</li>
+     * </ul>
+     */
+    private int severity = OK;
+
+    /** Unique identifier of plug-in.
+     */
+    private String pluginId;
+
+    /** Plug-in-specific status code.
+     */
+    private int code;
+
+    /** Message, localized to the current locale.
+     */
+    private String message;
+
+    /** Wrapped exception, or <code>null</code> if none.
+     */
+    private Exception exception = null;
+
+    /** Constant to avoid generating garbage.
+     */
+    private static const IStatus[] theEmptyStatusArray = null;
+
+    /**
+     * Creates a new status object.  The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param code the plug-in-specific status code, or <code>OK</code>
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    public this(int severity, String pluginId, int code, String message, Exception exception) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setCode(code);
+        setMessage(message);
+        setException(exception);
+    }
+
+    /**
+     * Simplified constructor of a new status object; assumes that code is <code>OK</code>.
+     * The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param message a human-readable message, localized to the
+     *    current locale
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     *
+     * @since dwtx.equinox.common 3.3
+     */
+    public this(int severity, String pluginId, String message, Exception exception) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setMessage(message);
+        setException(exception);
+        setCode(OK);
+    }
+
+    /**
+     * Simplified constructor of a new status object; assumes that code is <code>OK</code> and
+     * exception is <code>null</code>. The created status has no children.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     * @param pluginId the unique identifier of the relevant plug-in
+     * @param message a human-readable message, localized to the
+     *    current locale
+     *
+     * @since dwtx.equinox.common 3.3
+     */
+    public this(int severity, String pluginId, String message) {
+        setSeverity(severity);
+        setPlugin(pluginId);
+        setMessage(message);
+        setCode(OK);
+        setException(null);
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public IStatus[] getChildren() {
+        return theEmptyStatusArray;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public int getCode() {
+        return code;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public Exception getException() {
+        return exception;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public String getPlugin() {
+        return pluginId;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public int getSeverity() {
+        return severity;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool isMultiStatus() {
+        return false;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool isOK() {
+        return severity is OK;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the corresponding method on <code>IStatus</code>.
+     */
+    public bool matches(int severityMask) {
+        return (severity & severityMask) !is 0;
+    }
+
+    /**
+     * Sets the status code.
+     *
+     * @param code the plug-in-specific status code, or <code>OK</code>
+     */
+    protected void setCode(int code) {
+        this.code = code;
+    }
+
+    /**
+     * Sets the exception.
+     *
+     * @param exception a low-level exception, or <code>null</code> if not
+     *    applicable
+     */
+    protected void setException(Exception exception) {
+        this.exception = exception;
+    }
+
+    /**
+     * Sets the message. If null is passed, message is set to an empty
+     * string.
+     *
+     * @param message a human-readable message, localized to the
+     *    current locale
+     */
+    protected void setMessage(String message) {
+        if (message is null)
+            this.message = ""; //$NON-NLS-1$
+        else
+            this.message = message;
+    }
+
+    /**
+     * Sets the plug-in id.
+     *
+     * @param pluginId the unique identifier of the relevant plug-in
+     */
+    protected void setPlugin(String pluginId) {
+        Assert.isLegal(pluginId !is null && pluginId.length > 0);
+        this.pluginId = pluginId;
+    }
+
+    /**
+     * Sets the severity.
+     *
+     * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+     * <code>INFO</code>, <code>WARNING</code>,  or <code>CANCEL</code>
+     */
+    protected void setSeverity(int severity) {
+        Assert.isLegal(severity is OK || severity is ERROR || severity is WARNING || severity is INFO || severity is CANCEL);
+        this.severity = severity;
+    }
+
+    /**
+     * Returns a string representation of the status, suitable
+     * for debugging purposes only.
+     */
+    public String toString() {
+        String sev;
+        if (severity is OK) {
+            sev="OK"; //$NON-NLS-1$
+        } else if (severity is ERROR) {
+            sev="ERROR"; //$NON-NLS-1$
+        } else if (severity is WARNING) {
+            sev="WARNING"; //$NON-NLS-1$
+        } else if (severity is INFO) {
+            sev="INFO"; //$NON-NLS-1$
+        } else if (severity is CANCEL) {
+            sev="CANCEL"; //$NON-NLS-1$
+        } else {
+            sev=Format( "severity={}", severity);
+        }
+        return Format("Status {}: {} code={} {} {}", sev, pluginId, code, message, exception.toString );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/SubMonitor.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,787 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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:
+ *     Stefan Xenos - initial API and implementation
+ *     Stefan Xenos - bug 174539 - add a 1-argument convert(...) method
+ *     Stefan Xenos - bug 174040 - SubMonitor#convert doesn't always set task name
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.SubMonitor;
+
+import dwtx.core.runtime.IProgressMonitorWithBlocking;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.NullProgressMonitor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <p>A progress monitor that uses a given amount of work ticks from a parent monitor. This is intended as a
+ * safer, easier-to-use alternative to SubProgressMonitor. The main benefits of SubMonitor over
+ * SubProgressMonitor are:</p>
+ * <ul>
+ * <li>It is not necessary to call beginTask() or done() on an instance of SubMonitor.</li>
+ * <li>SubMonitor has a simpler syntax for creating nested monitors.</li>
+ * <li>SubMonitor is more efficient for deep recursion chains.</li>
+ * <li>SubMonitor has a setWorkRemining method that allows the remaining space on the monitor to be
+ * redistributed without reporting any work.</li>
+ * <li>SubMonitor protects the caller from common progress reporting bugs in a called method. For example,
+ * if a called method fails to call done() on the given monitor or fails to consume all the ticks on
+ * the given monitor, the parent will correct the problem after the method returns.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>USAGE:</b></p>
+ *
+ * <p>When implementing a method that accepts an IProgressMonitor:</p>
+ * <ul>
+ * <li>At the start of your method, use <code>SubMonitor.convert(...).</code> to convert the IProgressMonitor
+ * into a SubMonitor. </li>
+ * <li>Use <code>SubMonitor.newChild(...)</code> whenever you need to call another method that
+ * accepts an IProgressMonitor.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>DEFAULT BEHAVIOR:</b></p>
+ *
+ * <p>When writing JavaDoc for a method that accepts an IProgressMonitor, you should assume the
+ * following default behavior unless the method's JavaDoc says otherwise:</p>
+ * <ul>
+ * <li>It WILL call beginTask on the IProgressMonitor.</li>
+ * <li>It WILL NOT accept a null argument.</li>
+ * <li>It WILL call done on the IProgressMonitor.</li>
+ * </ul>
+ * <p></p>
+ * <p><b>BEST PRACTISES:</b></p>
+ *
+ * <p>We recommend that newly-written methods follow the given contract:</p>
+ * <ul>
+ * <li>It WILL call beginTask on the IProgressMonitor.</li>
+ * <li>It WILL accept a null argument, indicating that no progress should be reported and the operation cannot be cancelled.</li>
+ * <li>It WILL NOT call done on the IProgressMonitor, leaving this responsibility up to the caller.</li>
+ * </ul>
+ * <p>If you wish to follow these conventions, you may copy and paste the following text into your method's JavaDoc:</p>
+ *
+ * <pre>@param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ *        to call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
+ *        reported and that the operation cannot be cancelled.</pre>
+ *
+ * <p></p>
+ * <p><b>Example: Recommended usage</b></p>
+ *
+ * <p>This example demonstrates how the recommended usage of <code>SubMonitor</code> makes it unnecessary to call
+ * IProgressMonitor.done() in most situations.</p>
+ *
+ * <p>It is never necessary to call done() on a monitor obtained from <code>convert</code> or <code>progress.newChild()</code>.
+ * In this example, there is no guarantee that <code>monitor</code> is an instance of <code>SubMonitor</code>, making it
+ * necessary to call <code>monitor.done()</code>. The JavaDoc contract makes this the responsibility of the caller.</p>
+ *
+ * <pre>
+ *      // param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ *      //        to call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
+ *      //        reported and that the operation cannot be cancelled.
+ *      //
+ *      void doSomething(IProgressMonitor monitor) {
+ *          // Convert the given monitor into a progress instance
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          // Use 30% of the progress to do some work
+ *          doSomeWork(progress.newChild(30));
+ *
+ *          // Advance the monitor by another 30%
+ *          progress.worked(30);
+ *
+ *          // Use the remaining 40% of the progress to do some more work
+ *          doSomeWork(progress.newChild(40));
+ *      }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Default usage</b></p>
+ *
+ * <p>You will often need to implement a method that does not explicitly stipulate that calling done() is the responsibility
+ * of the caller. In this case, you should use the following pattern:</p>
+ *
+ * <pre>
+ *      // param monitor the progress monitor to use for reporting progress to the user, or <code>null</code> indicating
+ *      //        that no progress should be reported and the operation cannot be cancelled.
+ *      //
+ *      void doSomething(IProgressMonitor monitor) {
+ *          // Convert the given monitor into a progress instance
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *          try {
+ *              // Use 30% of the progress to do some work
+ *              doSomeWork(progress.newChild(30));
+ *
+ *              // Advance the monitor by another 30%
+ *              progress.worked(30);
+ *
+ *              // Use the remaining 40% of the progress to do some more work
+ *              doSomeWork(progress.newChild(40));
+ *
+ *          } finally {
+ *              if (monitor !is null) {
+ *                  monitor.done();
+ *              }
+ *          }
+ *      }
+ * </pre>
+ *
+ * <p></p>
+ * <p><b>Example: Branches</b></p>
+ *
+ * <p>This example demonstrates how to smoothly report progress in situations where some of the work is optional.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor) {
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          if (condition) {
+ *              // Use 50% of the progress to do some work
+ *              doSomeWork(progress.newChild(50));
+ *          }
+ *
+ *          // Don't report any work, but ensure that we have 50 ticks remaining on the progress monitor.
+ *          // If we already consumed 50 ticks in the above branch, this is a no-op. Otherwise, the remaining
+ *          // space in the monitor is redistributed into 50 ticks.
+ *
+ *          progress.setWorkRemaining(50);
+ *
+ *          // Use the remainder of the progress monitor to do the rest of the work
+ *          doSomeWork(progress.newChild(50));
+ *      }
+ * </pre>
+ *
+ * <p>Please beware of the following anti-pattern:</p>
+ *
+ * <pre>
+ *          if (condition) {
+ *              // Use 50% of the progress to do some work
+ *              doSomeWork(progress.newChild(50));
+ *          } else {
+ *              // Bad: Causes the progress monitor to appear to start at 50%, wasting half of the
+ *              // space in the monitor.
+ *              progress.worked(50);
+ *          }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Loops</b></p>
+ *
+ * <p>This example demonstrates how to report progress in a loop.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor, Collection someCollection) {
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          // Create a new progress monitor that uses 70% of the total progress and will allocate one tick
+ *          // for each element of the given collection.
+ *          SubMonitor loopProgress = progress.newChild(70).setWorkRemaining(someCollection.size());
+ *
+ *          for (Iterator iter = someCollection.iterator(); iter.hasNext();) {
+ *              Object next = iter.next();
+ *
+ *              doWorkOnElement(next, loopProgress.newChild(1));
+ *          }
+ *
+ *          // Use the remaining 30% of the progress monitor to do some work outside the loop
+ *          doSomeWork(progress.newChild(30));
+ *      }
+ * </pre>
+ *
+ *
+ * <p></p>
+ * <p><b>Example: Infinite progress</b></p>
+ *
+ * <p>This example demonstrates how to report logarithmic progress in situations where the number of ticks
+ * cannot be easily computed in advance.</p>
+ *
+ * <pre>
+ *      void doSomething(IProgressMonitor monitor, LinkedListNode node) {
+ *          SubMonitor progress = SubMonitor.convert(monitor, 100);
+ *
+ *          while (node !is null) {
+ *              // Regardless of the amount of progress reported so far,
+ *              // use 5% of the space remaining in the monitor to process the next node.
+ *              progress.setWorkRemaining(100);
+ *
+ *              doWorkOnElement(node, progress.newChild(5));
+ *
+ *              node = node.next;
+ *          }
+ *      }
+ * </pre>
+ *
+ * <p>
+ * This class can be used without OSGi running.
+ * </p>
+ *
+ * @since dwtx.equinox.common 3.3
+ */
+public final class SubMonitor : IProgressMonitorWithBlocking {
+
+    /**
+     * Minimum number of ticks to allocate when calling beginTask on an unknown IProgressMonitor.
+     * Pick a number that is big enough such that, no matter where progress is being displayed,
+     * the user would be unlikely to notice if progress were to be reported with higher accuracy.
+     */
+    private static final int MINIMUM_RESOLUTION = 1000;
+
+    /**
+     * The RootInfo struct holds information about the root progress monitor. A SubMonitor and
+     * its active descendents share the same RootInfo struct.
+     */
+    private static final class RootInfo {
+        private final IProgressMonitor root;
+
+        /**
+         * Remembers the last task name. Prevents us from setting the same task name multiple
+         * times in a row.
+         */
+        private String taskName = null;
+
+        /**
+         * Remembers the last subtask name. Prevents the SubMonitor from setting the same
+         * subtask string more than once in a row.
+         */
+        private String subTask_ = null;
+
+        /**
+         * Creates a RootInfo struct that delegates to the given progress
+         * monitor.
+         *
+         * @param root progress monitor to delegate to
+         */
+        public this(IProgressMonitor root) {
+            this.root = root;
+        }
+
+        public bool isCanceled() {
+            return root.isCanceled();
+        }
+
+        public void setCanceled(bool value) {
+            root.setCanceled(value);
+        }
+
+        public void setTaskName(String taskName) {
+            if (eq(taskName, this.taskName)) {
+                return;
+            }
+            this.taskName = taskName;
+            root.setTaskName(taskName);
+        }
+
+        public void subTask(String name) {
+            if (eq(subTask_, name)) {
+                return;
+            }
+
+            this.subTask_ = name;
+            root.subTask(name);
+        }
+
+        public void worked(int i) {
+            root.worked(i);
+        }
+
+        public void clearBlocked() {
+            if ( auto mon = cast(IProgressMonitorWithBlocking)root )
+                mon.clearBlocked();
+        }
+
+        public void setBlocked(IStatus reason) {
+            if ( auto mon = cast(IProgressMonitorWithBlocking)root )
+                mon.setBlocked(reason);
+        }
+
+    }
+
+    /**
+     * Total number of ticks that this progress monitor is permitted to consume
+     * from the root.
+     */
+    private int totalParent;
+
+    /**
+     * Number of ticks that this progress monitor has already reported in the root.
+     */
+    private int usedForParent = 0;
+
+    /**
+     * Number of ticks that have been consumed by this instance's children.
+     */
+    private double usedForChildren = 0.0;
+
+    /**
+     * Number of ticks allocated for this instance's children. This is the total number
+     * of ticks that may be passed into worked(int) or newChild(int).
+     */
+    private int totalForChildren;
+
+    /**
+     * Children created by newChild will be completed automatically the next time
+     * the parent progress monitor is touched. This points to the last incomplete child
+     * created with newChild.
+     */
+    private IProgressMonitor lastSubMonitor = null;
+
+    /**
+     * Used to communicate with the root of this progress monitor tree
+     */
+    private const RootInfo root;
+
+    /**
+     * A bitwise combination of the SUPPRESS_* flags.
+     */
+    private const int flags;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that the calls
+     * to subTask on the child should be ignored. Without this flag,
+     * calling subTask on the child will result in a call to subTask
+     * on its parent.
+     */
+    public static const int SUPPRESS_SUBTASK = 0x0001;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed into beginTask should be ignored. If this flag is
+     * specified, then the progress monitor instance will accept null
+     * as the first argument to beginTask. Without this flag, any
+     * string passed to beginTask will result in a call to
+     * setTaskName on the parent.
+     */
+    public static const int SUPPRESS_BEGINTASK = 0x0002;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed into setTaskName should be ignored. If this string
+     * is omitted, then a call to setTaskName on the child will
+     * result in a call to setTaskName on the parent.
+     */
+    public static const int SUPPRESS_SETTASKNAME = 0x0004;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed to setTaskName, subTask, and beginTask should all be ignored.
+     */
+    public static const int SUPPRESS_ALL_LABELS = SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK | SUPPRESS_SUBTASK;
+
+    /**
+     * May be passed as a flag to newChild. Indicates that strings
+     * passed to setTaskName, subTask, and beginTask should all be propogated
+     * to the parent.
+     */
+    public static const int SUPPRESS_NONE = 0;
+
+    /**
+     * Creates a new SubMonitor that will report its progress via
+     * the given RootInfo.
+     * @param rootInfo the root of this progress monitor tree
+     * @param totalWork total work to perform on the given progress monitor
+     * @param availableToChildren number of ticks allocated for this instance's children
+     * @param flags a bitwise combination of the SUPPRESS_* constants
+     */
+    private this(RootInfo rootInfo, int totalWork, int availableToChildren, int flags) {
+        root = rootInfo;
+        totalParent = (totalWork > 0) ? totalWork : 0;
+        this.totalForChildren = availableToChildren;
+        this.flags = flags;
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor. It is
+     * not necessary to call done() on the result, but the caller is responsible for calling
+     * done() on the argument. Calls beginTask on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor monitor to convert to a SubMonitor instance or null. Treats null
+     * as a new instance of <code>NullProgressMonitor</code>.
+     * @return a SubMonitor instance that adapts the argument
+     */
+    public static SubMonitor convert(IProgressMonitor monitor) {
+        return convert(monitor, "", 0); //$NON-NLS-1$
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
+     * with the given number of ticks. It is not necessary to call done() on the result,
+     * but the caller is responsible for calling done() on the argument. Calls beginTask
+     * on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor monitor to convert to a SubMonitor instance or null. Treats null
+     * as a new instance of <code>NullProgressMonitor</code>.
+     * @param work number of ticks that will be available in the resulting monitor
+     * @return a SubMonitor instance that adapts the argument
+     */
+    public static SubMonitor convert(IProgressMonitor monitor, int work) {
+        return convert(monitor, "", work); //$NON-NLS-1$
+    }
+
+    /**
+     * <p>Converts an unknown (possibly null) IProgressMonitor into a SubMonitor allocated
+     * with the given number of ticks. It is not necessary to call done() on the result,
+     * but the caller is responsible for calling done() on the argument. Calls beginTask
+     * on the argument.</p>
+     *
+     * <p>This method should generally be called at the beginning of a method that accepts
+     * an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+     *
+     * @param monitor to convert into a SubMonitor instance or null. If given a null argument,
+     * the resulting SubMonitor will not report its progress anywhere.
+     * @param taskName user readable name to pass to monitor.beginTask. Never null.
+     * @param work initial number of ticks to allocate for children of the SubMonitor
+     * @return a new SubMonitor instance that is a child of the given monitor
+     */
+    public static SubMonitor convert(IProgressMonitor monitor, String taskName, int work) {
+        if (monitor is null)
+            monitor = new NullProgressMonitor();
+
+        // Optimization: if the given monitor already a SubMonitor, no conversion is necessary
+        if ( cast(SubMonitor) monitor ) {
+            monitor.beginTask(taskName, work);
+            return cast(SubMonitor) monitor;
+        }
+
+        monitor.beginTask(taskName, MINIMUM_RESOLUTION);
+        return new SubMonitor(new RootInfo(monitor), MINIMUM_RESOLUTION, work, SUPPRESS_NONE);
+    }
+
+    /**
+     * <p>Sets the work remaining for this SubMonitor instance. This is the total number
+     * of ticks that may be reported by all subsequent calls to worked(int), newChild(int), etc.
+     * This may be called many times for the same SubMonitor instance. When this method
+     * is called, the remaining space on the progress monitor is redistributed into the given
+     * number of ticks.</p>
+     *
+     * <p>It doesn't matter how much progress has already been reported with this SubMonitor
+     * instance. If you call setWorkRemaining(100), you will be able to report 100 more ticks of
+     * work before the progress meter reaches 100%.</p>
+     *
+     * @param workRemaining total number of remaining ticks
+     * @return the receiver
+     */
+    public SubMonitor setWorkRemaining(int workRemaining) {
+        // Ensure we don't try to allocate negative ticks
+        workRemaining = Math.max(0, workRemaining);
+
+        // Ensure we don't cause division by zero
+        if (totalForChildren > 0 && totalParent > usedForParent) {
+            // Note: We want the following value to remain invariant after this method returns
+            double remainForParent = totalParent * (1.0 - (usedForChildren / totalForChildren));
+            usedForChildren = (workRemaining * (1.0 - remainForParent / (totalParent - usedForParent)));
+        } else
+            usedForChildren = 0.0;
+
+        totalParent = totalParent - usedForParent;
+        usedForParent = 0;
+        totalForChildren = workRemaining;
+        return this;
+    }
+
+    /**
+     * Consumes the given number of child ticks, given as a double. Must only
+     * be called if the monitor is in floating-point mode.
+     *
+     * @param ticks the number of ticks to consume
+     * @return ticks the number of ticks to be consumed from parent
+     */
+    private int consume(double ticks) {
+        if (totalParent is 0 || totalForChildren is 0) // this monitor has no available work to report
+            return 0;
+
+        usedForChildren += ticks;
+
+        if (usedForChildren > totalForChildren)
+            usedForChildren = totalForChildren;
+        else if (usedForChildren < 0.0)
+            usedForChildren = 0.0;
+
+        int parentPosition = cast(int) (totalParent * usedForChildren / totalForChildren);
+        int delta = parentPosition - usedForParent;
+
+        usedForParent = parentPosition;
+        return delta;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#isCanceled()
+     */
+    public bool isCanceled() {
+        return root.isCanceled();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
+     */
+    public void setTaskName(String name) {
+        if ((flags & SUPPRESS_SETTASKNAME) is 0)
+            root.setTaskName(name);
+    }
+
+    /**
+     * Starts a new main task. The string argument is ignored
+     * if and only if the SUPPRESS_BEGINTASK flag has been set on this SubMonitor
+     * instance.
+     *
+     * <p>This method is equivalent calling setWorkRemaining(...) on the reciever. Unless
+     * the SUPPRESS_BEGINTASK flag is set, this will also be equivalent to calling
+     * setTaskName(...) on the parent.</p>
+     *
+     * @param name new main task name
+     * @param totalWork number of ticks to allocate
+     *
+     * @see dwtx.core.runtime.IProgressMonitor#beginTask(java.lang.String, int)
+     */
+    public void beginTask(String name, int totalWork) {
+        if ((flags & SUPPRESS_BEGINTASK) is 0 && name !is null)
+            root.setTaskName(name);
+        setWorkRemaining(totalWork);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#done()
+     */
+    public void done() {
+        cleanupActiveChild();
+        int delta = totalParent - usedForParent;
+        if (delta > 0)
+            root.worked(delta);
+
+        totalParent = 0;
+        usedForParent = 0;
+        totalForChildren = 0;
+        usedForChildren = 0.0;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#internalWorked(double)
+     */
+    public void internalWorked(double work) {
+        int delta = consume((work > 0.0) ? work : 0.0);
+        if (delta !is 0)
+            root.worked(delta);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#subTask(java.lang.String)
+     */
+    public void subTask(String name) {
+        if ((flags & SUPPRESS_SUBTASK) is 0)
+            root.subTask(name);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#worked(int)
+     */
+    public void worked(int work) {
+        internalWorked(work);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitor#setCanceled(bool)
+     */
+    public void setCanceled(bool b) {
+        root.setCanceled(b);
+    }
+
+    /**
+     * <p>Creates a sub progress monitor that will consume the given number of ticks from the
+     * receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
+     * result. However, the resulting progress monitor will not report any work after the first
+     * call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
+     * or setWorkRemaining.</p>
+     *
+     * <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
+     * result becomes the new active child and any unused progress from the previously-active child is
+     * consumed.</p>
+     *
+     * <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
+     * monitors are automatically cleaned up the next time the parent is touched.</p>
+     *
+     * <code><pre>
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 1: Typical usage of newChild
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *          doSomething(progress.newChild(50));
+     *          doSomethingElse(progress.newChild(50));
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 2: Demonstrates the function of active children. Creating children
+     *      // is sufficient to smoothly report progress, even if worked(...) and done()
+     *      // are never called.
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          for (int i = 0; i < 100; i++) {
+     *              // Creating the next child monitor will clean up the previous one,
+     *              // causing progress to be reported smoothly even if we don't do anything
+     *              // with the monitors we create
+     *              progress.newChild(1);
+     *          }
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 3: Demonstrates a common anti-pattern
+     *      void wrongMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // WRONG WAY: Won't have the intended effect, as only one of these progress
+     *          // monitors may be active at a time and the other will report no progress.
+     *          callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
+     *      }
+     *
+     *      void rightMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
+     *          Object someValue = computeValue(progress.newChild(50));
+     *          callMethod(progress.newChild(50), someValue);
+     *      }
+     * </pre></code>
+     *
+     * @param totalWork number of ticks to consume from the reciever
+     * @return new sub progress monitor that may be used in place of a new SubMonitor
+     */
+    public SubMonitor newChild(int totalWork) {
+        return newChild(totalWork, SUPPRESS_BEGINTASK);
+    }
+
+    /**
+     * <p>Creates a sub progress monitor that will consume the given number of ticks from the
+     * receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
+     * result. However, the resulting progress monitor will not report any work after the first
+     * call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
+     * or setWorkRemaining.</p>
+     *
+     * <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
+     * result becomes the new active child and any unused progress from the previously-active child is
+     * consumed.</p>
+     *
+     * <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
+     * monitors are automatically cleaned up the next time the parent is touched.</p>
+     *
+     * <code><pre>
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 1: Typical usage of newChild
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *          doSomething(progress.newChild(50));
+     *          doSomethingElse(progress.newChild(50));
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 2: Demonstrates the function of active children. Creating children
+     *      // is sufficient to smoothly report progress, even if worked(...) and done()
+     *      // are never called.
+     *      void myMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          for (int i = 0; i < 100; i++) {
+     *              // Creating the next child monitor will clean up the previous one,
+     *              // causing progress to be reported smoothly even if we don't do anything
+     *              // with the monitors we create
+     *              progress.newChild(1);
+     *          }
+     *      }
+     *
+     *      ////////////////////////////////////////////////////////////////////////////
+     *      // Example 3: Demonstrates a common anti-pattern
+     *      void wrongMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // WRONG WAY: Won't have the intended effect, as only one of these progress
+     *          // monitors may be active at a time and the other will report no progress.
+     *          callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
+     *      }
+     *
+     *      void rightMethod(IProgressMonitor parent) {
+     *          SubMonitor progress = SubMonitor.convert(parent, 100);
+     *
+     *          // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
+     *          Object someValue = computeValue(progress.newChild(50));
+     *          callMethod(progress.newChild(50), someValue);
+     *      }
+     * </pre></code>
+     *
+     * @param totalWork number of ticks to consume from the reciever
+     * @return new sub progress monitor that may be used in place of a new SubMonitor
+     */
+    public SubMonitor newChild(int totalWork, int suppressFlags) {
+        double totalWorkDouble = (totalWork > 0) ? totalWork : 0.0;
+        totalWorkDouble = Math.min(totalWorkDouble, totalForChildren - usedForChildren);
+        cleanupActiveChild();
+
+        // Compute the flags for the child. We want the net effect to be as though the child is
+        // delegating to its parent, even though it is actually talking directly to the root.
+        // This means that we need to compute the flags such that - even if a label isn't
+        // suppressed by the child - if that same label would have been suppressed when the
+        // child delegated to its parent, the child must explicitly suppress the label.
+        int childFlags = SUPPRESS_NONE;
+
+        if ((flags & SUPPRESS_SETTASKNAME) !is 0) {
+            // If the parent was ignoring labels passed to setTaskName, then the child will ignore
+            // labels passed to either beginTask or setTaskName - since both delegate to setTaskName
+            // on the parent
+            childFlags |= SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK;
+        }
+
+        if ((flags & SUPPRESS_SUBTASK) !is 0) {
+            // If the parent was suppressing labels passed to subTask, so will the child.
+            childFlags |= SUPPRESS_SUBTASK;
+        }
+
+        // Note: the SUPPRESS_BEGINTASK flag does not affect the child since there
+        // is no method on the child that would delegate to beginTask on the parent.
+        childFlags |= suppressFlags;
+
+        SubMonitor result = new SubMonitor(root, consume(totalWorkDouble), 0, childFlags);
+        lastSubMonitor = result;
+        return result;
+    }
+
+    private void cleanupActiveChild() {
+        if (lastSubMonitor is null)
+            return;
+
+        IProgressMonitor child = lastSubMonitor;
+        lastSubMonitor = null;
+        child.done();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
+     */
+    public void clearBlocked() {
+        root.clearBlocked();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.IProgressMonitorWithBlocking#setBlocked(dwtx.core.runtime.IStatus)
+     */
+    public void setBlocked(IStatus reason) {
+        root.setBlocked(reason);
+    }
+
+    protected static bool eq(String o1, String o2) {
+        if (o1.length is 0)
+            return (o2.length is 0);
+        if (o2.length is 0)
+            return false;
+        return o1.equals(o2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/SubProgressMonitor.d	Wed Mar 26 00:57:19 2008 +0100
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 dwtx.core.runtime.SubProgressMonitor;
+
+import dwtx.core.runtime.ProgressMonitorWrapper;
+import dwtx.core.runtime.IProgressMonitor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * For new implementations consider using {@link SubMonitor}.
+ *
+ * A progress monitor that uses a given amount of work ticks
+ * from a parent monitor. It can be used as follows:
+ * <pre>
+ *     try {
+ *         pm.beginTask("Main Task", 100);
+ *         doSomeWork(pm, 30);
+ *         SubProgressMonitor subMonitor= new SubProgressMonitor(pm, 40);
+ *         try {
+ *             subMonitor.beginTask("", 300);
+ *             doSomeWork(subMonitor, 300);
+ *         } finally {
+ *             subMonitor.done();
+ *         }
+ *         doSomeWork(pm, 30);
+ *     } finally {
+ *         pm.done();
+ *     }
+ * </pre>
+ * <p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * This class may be instantiated or subclassed by clients.
+ * </p>
+ *
+ * @see SubMonitor
+ */
+public class SubProgressMonitor : ProgressMonitorWrapper {
+
+    /**
+     * Style constant indicating that calls to <code>subTask</code>
+     * should not have any effect.
+     *
+     * @see #SubProgressMonitor(IProgressMonitor,int,int)
+     */
+    public static const int SUPPRESS_SUBTASK_LABEL = 1 << 1;
+    /**
+     * Style constant indicating that the main task label
+     * should be prepended to the subtask label.
+     *
+     * @see #SubProgressMonitor(IProgressMonitor,int,int)
+     */
+    public static const int PREPEND_MAIN_LABEL_TO_SUBTASK = 1 << 2;
+
+    private int parentTicks = 0;
+    private double sentToParent = 0.0;
+    private double scale = 0.0;
+    private int nestedBeginTasks = 0;
+    private bool usedUp = false;
+    private bool hasSubTask = false;
+    private int style;
+    private String mainTaskLabel;
+
+    /**
+     * Creates a new sub-progress monitor for the given monitor. The sub
+     * progress monitor uses the given number of work ticks from its
+     * parent monitor.
+     *
+     * @param monitor the parent progress monitor
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor
+     */
+    public this(IProgressMonitor monitor, int ticks) {
+        this(monitor, ticks, 0);
+    }
+
+    /**
+     * Creates a new sub-progress monitor for the given monitor. The sub
+     * progress monitor uses the given number of work ticks from its
+     * parent monitor.
+     *
+     * @param monitor the parent progress monitor
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor
+     * @param style one of
+     *    <ul>
+     *    <li> <code>SUPPRESS_SUBTASK_LABEL</code> </li>
+     *    <li> <code>PREPEND_MAIN_LABEL_TO_SUBTASK</code> </li>
+     *    </ul>
+     * @see #SUPPRESS_SUBTASK_LABEL
+     * @see #PREPEND_MAIN_LABEL_TO_SUBTASK
+     */
+    public this(IProgressMonitor monitor, int ticks, int style) {
+        super(monitor);
+        this.parentTicks = (ticks > 0) ? ticks : 0;
+        this.style = style;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.beginTask</code>.
+     *
+     * Starts a new main task. Since this progress monitor is a sub
+     * progress monitor, the given name will NOT be used to update
+     * the progress bar's main task label. That means the given
+     * string will be ignored. If style <code>PREPEND_MAIN_LABEL_TO_SUBTASK
+     * <code> is specified, then the given string will be prepended to
+     * every string passed to <code>subTask(String)</code>.
+     */
+    public void beginTask(String name, int totalWork) {
+        nestedBeginTasks++;
+        // Ignore nested begin task calls.
+        if (nestedBeginTasks > 1) {
+            return;
+        }
+        // be safe:  if the argument would cause math errors (zero or
+        // negative), just use 0 as the scale.  This disables progress for
+        // this submonitor.
+        scale = totalWork <= 0 ? 0 : cast(double) parentTicks / cast(double) totalWork;
+        if ((style & PREPEND_MAIN_LABEL_TO_SUBTASK) !is 0) {
+            mainTaskLabel = name;
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.done</code>.
+     */
+    public void done() {
+        // Ignore if more done calls than beginTask calls or if we are still
+        // in some nested beginTasks
+        if (nestedBeginTasks is 0 || --nestedBeginTasks > 0)
+            return;
+        // Send any remaining ticks and clear out the subtask text
+        double remaining = parentTicks - sentToParent;
+        if (remaining > 0)
+            super.internalWorked(remaining);
+        //clear the sub task if there was one
+        if (hasSubTask)
+            subTask(""); //$NON-NLS-1$
+        sentToParent = 0;
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the internal method <code>IProgressMonitor.internalWorked</code>.
+     */
+    public void internalWorked(double work) {
+        if (usedUp || nestedBeginTasks !is 1) {
+            return;
+        }
+
+        double realWork = (work > 0.0) ? scale * work : 0.0;
+        super.internalWorked(realWork);
+        sentToParent += realWork;
+        if (sentToParent >= parentTicks) {
+            usedUp = true;
+        }
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.subTask</code>.
+     */
+    public void subTask(String name) {
+        if ((style & SUPPRESS_SUBTASK_LABEL) !is 0) {
+            return;
+        }
+        hasSubTask = true;
+        String label = name;
+        if ((style & PREPEND_MAIN_LABEL_TO_SUBTASK) !is 0 && mainTaskLabel !is null && mainTaskLabel.length > 0) {
+            label = mainTaskLabel ~ ' ' ~ label;
+        }
+        super.subTask(label);
+    }
+
+    /* (Intentionally not javadoc'd)
+     * Implements the method <code>IProgressMonitor.worked</code>.
+     */
+    public void worked(int work) {
+        internalWorked(work);
+    }
+}