Mercurial > projects > dwt-addons
view dwtx/core/commands/ParameterizedCommand.d @ 104:04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
These new wrappers now use the tango.util.containers instead of the tango.util.collections.
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Thu, 07 Aug 2008 15:01:33 +0200 |
parents | 4878bef4a38e |
children |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2005, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Benjamin Muskalla - bug 222861 [Commands] ParameterizedCommand#equals broken * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.core.commands.ParameterizedCommand; import dwtx.core.commands.AbstractParameterValueConverter; import dwtx.core.commands.Command; import dwtx.core.commands.CommandManager; import dwtx.core.commands.IParameter; import dwtx.core.commands.IParameterValues; import dwtx.core.commands.ParameterType; import dwtx.core.commands.Parameterization; import dwtx.core.commands.ParameterValuesException; import dwtx.core.commands.ParameterValueConversionException; import dwtx.core.commands.ExecutionEvent; import dwtx.core.commands.common.NotDefinedException; import dwtx.core.internal.commands.util.Util; import dwt.dwthelper.utils; import dwtx.dwtxhelper.Collection; 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 { /** * 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 ); } /** * 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 * @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 Collection expandParameters(int startIndex, IParameter[] parameters) { int nextIndex = startIndex + 1; bool noMoreParameters = (nextIndex >= parameters.length); IParameter parameter = parameters[startIndex]; List parameterizations = new ArrayList(); if (parameter.isOptional()) { parameterizations.add( cast(Object) null); } IParameterValues values = null; try { values = parameter.getValues(); } catch (ParameterValuesException e) { if (noMoreParameters) { return parameterizations; } // Make recursive call return expandParameters(nextIndex, parameters); } Map parameterValues = values.getParameterValues(); Iterator parameterValueItr = parameterValues.entrySet() .iterator(); while (parameterValueItr.hasNext()) { Map.Entry entry = cast(Map.Entry) parameterValueItr.next(); Parameterization parameterization = new Parameterization( parameter, stringcast( entry.getValue())); parameterizations.add(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); List combination = new ArrayList(); combination.add(parameterization); parameterizations.set(i, cast(Object)combination); } return parameterizations; } // Make recursive call Collection suffixes = expandParameters(nextIndex, parameters); while (suffixes.remove(cast(Object)null)) { // just keep deleting the darn things. } if (suffixes.isEmpty()) { // This is it, so just return the current parameterizations. for (int i = 0; i < parameterizationCount; i++) { Parameterization parameterization = cast(Parameterization) parameterizations .get(i); List combination = new ArrayList(); combination.add(parameterization); parameterizations.set(i,cast(Object) combination); } return parameterizations; } Collection returnValue = new ArrayList(); Iterator suffixItr = suffixes.iterator(); while (suffixItr.hasNext()) { List combination = cast(List) suffixItr.next(); int combinationSize = combination.size(); for (int i = 0; i < parameterizationCount; i++) { Parameterization parameterization = cast(Parameterization) parameterizations .get(i); List newCombination = new ArrayList(combinationSize + 1); newCombination.add(parameterization); newCombination.addAll(combination); returnValue.add(cast(Object)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 Collection generateCombinations(Command command) { IParameter[] parameters = command.getParameters(); if (parameters is null) { return Collections .singleton(new ParameterizedCommand(command, null)); } Collection expansion = expandParameters(0, parameters); Collection combinations = new ArrayList(expansion.size()); Iterator expansionItr = expansion.iterator(); while (expansionItr.hasNext()) { List combination = cast(List) expansionItr.next(); if (combination is null) { combinations.add(new ParameterizedCommand(command, null)); } else { while (combination.remove(cast(Object)null)) { // Just keep removing while there are null entries left. } if (combination.isEmpty()) { combinations.add(new ParameterizedCommand(command, null)); } else { Parameterization[] parameterizations = arraycast!(Parameterization)( combination .toArray()); combinations.add(new ParameterizedCommand(command, parameterizations)); } } } return combinations; } /** * Take a command and a map of parameter IDs to values, and generate the * appropriate parameterized command. * * @param command * The command object. Must not be <code>null</code>. * @param parameters * A map of String parameter ids to objects. May be * <code>null</code>. * @return the parameterized command, or <code>null</code> if it could not * be generated * @since 3.4 */ public static final ParameterizedCommand generateCommand(Command command, Map parameters) { // no parameters if (parameters is null || parameters.isEmpty()) { return new ParameterizedCommand(command, null); } try { ArrayList parms = new ArrayList(); Iterator i = parameters.keySet().iterator(); // iterate over given parameters while (i.hasNext()) { String key = stringcast( i.next() ); IParameter parameter = null; // get the parameter from the command parameter = command.getParameter(key); // if the parameter is defined add it to the parameter list if (parameter is null) { return null; } ParameterType parameterType = command.getParameterType(key); if (parameterType is null) { parms.add(new Parameterization(parameter, stringcast( parameters.get(key)))); } else { AbstractParameterValueConverter valueConverter = parameterType .getValueConverter(); if (valueConverter !is null) { String val = valueConverter.convertToString( parameters .get(key)); parms.add(new Parameterization(parameter, val)); } else { parms.add(new Parameterization(parameter, stringcast(parameters.get(key)))); } } } // convert the parameters to an Parameterization array and create // the command return new ParameterizedCommand(command, arraycast!(Parameterization)( parms .toArray())); } catch (NotDefinedException e) { } catch (ParameterValueConversionException e) { } return null; } /** * 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>. */ 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; IParameter[] parms = null; try { parms = command.getParameters(); } catch (NotDefinedException e) { // This should not happen. } if (parameterizations !is null && parameterizations.length>0 && parms !is null) { int parmIndex = 0; Parameterization[] params = new Parameterization[parameterizations.length]; for (int j = 0; j < parms.length; j++) { for (int i = 0; i < parameterizations.length; i++) { Parameterization pm = parameterizations[i]; if ((cast(Object)parms[j]).opEquals(cast(Object)pm.getParameter())) { params[parmIndex++] = pm; } } } this.parameterizations = params; } else { this.parameterizations = null; } } /* * (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 override final int opEquals(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 getParameterMap() { if ((parameterizations is null) || (parameterizations.length is 0)) { return Collections.EMPTY_MAP; } Map parameterMap = new HashMap(); for (int i = 0; i < parameterizations.length; i++) { Parameterization parameterization = parameterizations[i]; parameterMap.put(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); } }