diff dwtx/core/commands/ParameterizedCommand.d @ 3:6518c18a01f7

eclipse.core package without osgi dependencies
author Frank Benoit <benoit@tionex.de>
date Wed, 26 Mar 2008 00:57:19 +0100
parents
children ea8ff534f622
line wrap: on
line diff
--- /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);
+    }
+}