comparison org.eclipse.core.commands/src/org/eclipse/core/commands/CommandManager.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.core.commands.CommandManager;
14
15 import org.eclipse.core.commands.common.HandleObjectManager;
16 import org.eclipse.core.commands.common.NotDefinedException;
17 import org.eclipse.core.runtime.ListenerList;
18
19 import org.eclipse.core.commands.ICategoryListener;
20 import org.eclipse.core.commands.ICommandListener;
21 import org.eclipse.core.commands.IExecutionListener;
22 import org.eclipse.core.commands.IExecutionListenerWithChecks;
23 import org.eclipse.core.commands.IParameterTypeListener;
24 import org.eclipse.core.commands.ICommandManagerListener;
25 import org.eclipse.core.commands.CommandEvent;
26 import org.eclipse.core.commands.CommandManagerEvent;
27 import org.eclipse.core.commands.Command;
28 import org.eclipse.core.commands.Category;
29 import org.eclipse.core.commands.CategoryEvent;
30 import org.eclipse.core.commands.IHandler;
31 import org.eclipse.core.commands.ParameterType;
32 import org.eclipse.core.commands.IParameter;
33 import org.eclipse.core.commands.Parameterization;
34 import org.eclipse.core.commands.ParameterizedCommand;
35 import org.eclipse.core.commands.ParameterTypeEvent;
36 import org.eclipse.core.commands.SerializationException;
37
38 import org.eclipse.core.commands.NotEnabledException;
39 import org.eclipse.core.commands.NotHandledException;
40 import org.eclipse.core.commands.ExecutionException;
41 import org.eclipse.core.commands.ExecutionEvent;
42
43 import java.lang.all;
44
45 import java.util.WeakHashMap;
46 import java.util.HashMap;
47 import java.util.Map;
48 import java.util.Iterator;
49 import java.util.Collections;
50 import java.util.ArrayList;
51 import java.util.Set;
52 import java.util.HashSet;
53
54 /**
55 * <p>
56 * A central repository for commands -- both in the defined and undefined
57 * states. Commands can be created and retrieved using this manager. It is
58 * possible to listen to changes in the collection of commands by attaching a
59 * listener to the manager.
60 * </p>
61 *
62 * @see CommandManager#getCommand(String)
63 * @since 3.1
64 */
65 public final class CommandManager : HandleObjectManager,
66 ICategoryListener, ICommandListener, IParameterTypeListener {
67
68 /**
69 * A listener that forwards incoming execution events to execution listeners
70 * on this manager. The execution events will come from any command on this
71 * manager.
72 *
73 * @since 3.1
74 */
75 private final class ExecutionListener :
76 IExecutionListenerWithChecks {
77
78 public void notDefined(String commandId, NotDefinedException exception) {
79 if (executionListeners !is null) {
80 Object[] listeners = executionListeners.getListeners();
81 for (int i = 0; i < listeners.length; i++) {
82 Object object = listeners[i];
83 if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
84 listener.notDefined(commandId, exception);
85 }
86 }
87 }
88 }
89
90 public void notEnabled(String commandId, NotEnabledException exception) {
91 if (executionListeners !is null) {
92 Object[] listeners = executionListeners.getListeners();
93 for (int i = 0; i < listeners.length; i++) {
94 Object object = listeners[i];
95 if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
96 listener.notEnabled(commandId, exception);
97 }
98 }
99 }
100 }
101
102 public final void notHandled(String commandId,
103 NotHandledException exception) {
104 if (executionListeners !is null) {
105 Object[] listeners = executionListeners.getListeners();
106 for (int i = 0; i < listeners.length; i++) {
107 Object object = listeners[i];
108 if ( auto listener = cast(IExecutionListener)object ) {
109 listener.notHandled(commandId, exception);
110 }
111 }
112 }
113 }
114
115 public final void postExecuteFailure(String commandId,
116 ExecutionException exception) {
117 if (executionListeners !is null) {
118 Object[] listeners = executionListeners.getListeners();
119 for (int i = 0; i < listeners.length; i++) {
120 Object object = listeners[i];
121 if ( auto listener = cast(IExecutionListener)object ) {
122 listener.postExecuteFailure(commandId, exception);
123 }
124 }
125 }
126 }
127
128 public final void postExecuteSuccess(String commandId,
129 Object returnValue) {
130 if (executionListeners !is null) {
131 Object[] listeners = executionListeners.getListeners();
132 for (int i = 0; i < listeners.length; i++) {
133 Object object = listeners[i];
134 if ( auto listener = cast(IExecutionListener)object ) {
135 listener.postExecuteSuccess(commandId, returnValue);
136 }
137 }
138 }
139 }
140
141 public final void preExecute(String commandId,
142 ExecutionEvent event) {
143 if (executionListeners !is null) {
144 Object[] listeners = executionListeners.getListeners();
145 for (int i = 0; i < listeners.length; i++) {
146 Object object = listeners[i];
147 if ( auto listener = cast(IExecutionListener)object ) {
148 listener.preExecute(commandId, event);
149 }
150 }
151 }
152 }
153 }
154
155 /**
156 * The identifier of the category in which all auto-generated commands will
157 * appear. This value must never be <code>null</code>.
158 *
159 * @since 3.2
160 */
161 public static const String AUTOGENERATED_CATEGORY_ID = "org.eclipse.core.commands.categories.autogenerated"; //$NON-NLS-1$
162
163 /**
164 * The escape character to use for serialization and deserialization of
165 * parameterized commands.
166 */
167 static const char ESCAPE_CHAR = '%';
168
169 /**
170 * The character that separates a parameter id from its value.
171 */
172 static const char ID_VALUE_CHAR = '=';
173
174 /**
175 * The character that indicates the end of a list of parameters.
176 */
177 static const char PARAMETER_END_CHAR = ')';
178
179 /**
180 * The character that separators parameters from each other.
181 */
182 static const char PARAMETER_SEPARATOR_CHAR = ',';
183
184 /**
185 * The character that indicates the start of a list of parameters.
186 */
187 static const char PARAMETER_START_CHAR = '(';
188
189 this(){
190 categoriesById = new HashMap();
191 definedCategoryIds = new HashSet();
192 definedParameterTypeIds = new HashSet();
193 helpContextIdsByHandler = new WeakHashMap();
194 parameterTypesById = new HashMap();
195 }
196
197 /**
198 * Unescapes special characters in the command id, parameter ids and
199 * parameter values for {@link #deserialize(String)}. The special characters
200 * {@link #PARAMETER_START_CHAR}, {@link #PARAMETER_END_CHAR},
201 * {@link #ID_VALUE_CHAR}, {@link #PARAMETER_SEPARATOR_CHAR} and
202 * {@link #ESCAPE_CHAR} are escaped by prepending an {@link #ESCAPE_CHAR}
203 * character.
204 * <p>
205 * See also ParameterizedCommand.escape(String)
206 * </p>
207 *
208 * @param escapedText
209 * a <code>String</code> that may contain escaped special
210 * characters for command serialization.
211 * @return a <code>String</code> representing <code>escapedText</code>
212 * with any escaped characters replaced by their literal values
213 * @throws SerializationException
214 * if <code>escapedText</code> contains an invalid escape
215 * sequence
216 * @since 3.2
217 */
218 private static final String unescape(String escapedText) {
219
220 // defer initialization of a StringBuffer until we know we need one
221 StringBuffer buffer;
222
223 for (int i = 0; i < escapedText.length; i++) {
224
225 char c = escapedText.charAt(i);
226 if (c !is ESCAPE_CHAR) {
227 // normal unescaped character
228 if (buffer !is null) {
229 buffer.append(c);
230 }
231 } else {
232 if (buffer is null) {
233 buffer = new StringBuffer(escapedText.substring(0, i));
234 }
235
236 if (++i < escapedText.length) {
237 c = escapedText.charAt(i);
238 switch (c) {
239 case PARAMETER_START_CHAR:
240 case PARAMETER_END_CHAR:
241 case ID_VALUE_CHAR:
242 case PARAMETER_SEPARATOR_CHAR:
243 case ESCAPE_CHAR:
244 buffer.append(c);
245 break;
246 default:
247 throw new SerializationException(
248 "Invalid character '" ~ c ~ "' in escape sequence"); //$NON-NLS-1$ //$NON-NLS-2$
249 }
250 } else {
251 throw new SerializationException(
252 "Unexpected termination of escape sequence"); //$NON-NLS-1$
253 }
254 }
255
256 }
257
258 if (buffer is null) {
259 return escapedText;
260 }
261
262 return buffer.toString();
263 }
264
265 /**
266 * The map of category identifiers (<code>String</code>) to categories (
267 * <code>Category</code>). This collection may be empty, but it is never
268 * <code>null</code>.
269 */
270 private const Map categoriesById;
271
272 /**
273 * The set of identifiers for those categories that are defined. This value
274 * may be empty, but it is never <code>null</code>.
275 */
276 private const Set definedCategoryIds;
277
278 /**
279 * The set of identifiers for those command parameter types that are
280 * defined. This value may be empty, but it is never <code>null</code>.
281 *
282 * @since 3.2
283 */
284 private const Set definedParameterTypeIds;
285
286 /**
287 * The execution listener for this command manager. This just forwards
288 * events from commands controlled by this manager to listeners on this
289 * manager.
290 */
291 private IExecutionListenerWithChecks executionListener = null;
292
293 /**
294 * The collection of execution listeners. This collection is
295 * <code>null</code> if there are no listeners.
296 */
297 private ListenerList executionListeners = null;
298
299 /**
300 * The help context identifiers ({@link String}) for a handler ({@link IHandler}).
301 * This map may be empty, but it is never <code>null</code>. Entries are
302 * removed if all strong references to the handler are removed.
303 *
304 * @since 3.2
305 */
306 private const WeakHashMap helpContextIdsByHandler;
307
308 /**
309 * The map of parameter type identifiers (<code>String</code>) to
310 * parameter types ( <code>ParameterType</code>). This collection may be
311 * empty, but it is never <code>null</code>.
312 *
313 * @since 3.2
314 */
315 private const Map parameterTypesById;
316
317 /**
318 * Adds a listener to this command manager. The listener will be notified
319 * when the set of defined commands changes. This can be used to track the
320 * global appearance and disappearance of commands.
321 *
322 * @param listener
323 * The listener to attach; must not be <code>null</code>.
324 */
325 public final void addCommandManagerListener(
326 ICommandManagerListener listener) {
327 addListenerObject(cast(Object)listener);
328 }
329
330 /**
331 * Adds an execution listener to this manager. This listener will be
332 * notified if any of the commands controlled by this manager execute. This
333 * can be used to support macros and instrumentation of commands.
334 *
335 * @param listener
336 * The listener to attach; must not be <code>null</code>.
337 */
338 public final void addExecutionListener(IExecutionListener listener) {
339 if (listener is null) {
340 throw new NullPointerException(
341 "Cannot add a null execution listener"); //$NON-NLS-1$
342 }
343
344 if (executionListeners is null) {
345 executionListeners = new ListenerList(ListenerList.IDENTITY);
346
347 // Add an execution listener to every command.
348 executionListener = new ExecutionListener();
349 Iterator commandItr = handleObjectsById.values().iterator();
350 while (commandItr.hasNext()) {
351 Command command = cast(Command) commandItr.next();
352 command.addExecutionListener(executionListener);
353 }
354
355 }
356
357 executionListeners.add(cast(Object)listener);
358 }
359
360 /*
361 * (non-Javadoc)
362 *
363 * @see org.eclipse.core.commands.ICategoryListener#categoryChanged(org.eclipse.core.commands.CategoryEvent)
364 */
365 public final void categoryChanged(CategoryEvent categoryEvent) {
366 if (categoryEvent.isDefinedChanged()) {
367 Category category = categoryEvent.getCategory();
368 String categoryId = category.getId();
369 bool categoryIdAdded = category.isDefined();
370 if (categoryIdAdded) {
371 definedCategoryIds.add(categoryId);
372 } else {
373 definedCategoryIds.remove(categoryId);
374 }
375 if (isListenerAttached()) {
376 fireCommandManagerChanged(new CommandManagerEvent(this, null,
377 false, false, categoryId, categoryIdAdded, true));
378 }
379 }
380 }
381
382 /*
383 * (non-Javadoc)
384 *
385 * @see org.eclipse.commands.ICommandListener#commandChanged(org.eclipse.commands.CommandEvent)
386 */
387 public final void commandChanged(CommandEvent commandEvent) {
388 if (commandEvent.isDefinedChanged()) {
389 Command command = commandEvent.getCommand();
390 String commandId = command.getId();
391 bool commandIdAdded = command.isDefined();
392 if (commandIdAdded) {
393 definedHandleObjects.add(command);
394 } else {
395 definedHandleObjects.remove(command);
396 }
397 if (isListenerAttached()) {
398 fireCommandManagerChanged(new CommandManagerEvent(this,
399 commandId, commandIdAdded, true, null, false, false));
400 }
401 }
402 }
403
404 /**
405 * Sets the name and description of the category for uncategorized commands.
406 * This is the category that will be returned if
407 * {@link #getCategory(String)} is called with <code>null</code>.
408 *
409 * @param name
410 * The name of the category for uncategorized commands; must not
411 * be <code>null</code>.
412 * @param description
413 * The description of the category for uncategorized commands;
414 * may be <code>null</code>.
415 * @since 3.2
416 */
417 public final void defineUncategorizedCategory(String name,
418 String description) {
419 Category category = getCategory(AUTOGENERATED_CATEGORY_ID);
420 category.define(name, description);
421 }
422
423 /**
424 * <p>
425 * Returns a {@link ParameterizedCommand} with a command and
426 * parameterizations as specified in the provided
427 * <code>serializedParameterizedCommand</code> string. The
428 * <code>serializedParameterizedCommand</code> must use the format
429 * returned by {@link ParameterizedCommand#serialize()} and described in the
430 * Javadoc for that method.
431 * </p>
432 * <p>
433 * If a parameter id encoded in the
434 * <code>serializedParameterizedCommand</code> does not exist in the
435 * encoded command, that parameter id and value are ignored. A given
436 * parameter id should not be used more than once in
437 * <code>serializedParameterizedCommand</code>. This will not result in
438 * an exception, but in this case the value of the parameter when the
439 * command is executed is unspecified.
440 * </p>
441 * <p>
442 * This method will never return <code>null</code>, however it may throw
443 * an exception if there is a problem processing the serialization string or
444 * the encoded command is undefined.
445 * </p>
446 *
447 * @param serializedParameterizedCommand
448 * a string representing a command id and parameter ids and
449 * values; must not be <code>null</code>
450 * @return a {@link ParameterizedCommand} with the command and
451 * parameterizations encoded in the
452 * <code>serializedParameterizedCommand</code>; never
453 * <code>null</code>.
454 * @throws NotDefinedException
455 * if the command indicated in
456 * <code>serializedParameterizedCommand</code> is not defined
457 * @throws SerializationException
458 * if there is an error deserializing
459 * <code>serializedParameterizedCommand</code>
460 * @see ParameterizedCommand#serialize()
461 * @since 3.2
462 */
463 public final ParameterizedCommand deserialize(
464 String serializedParameterizedCommand) {
465
466 int lparenPosition = unescapedIndexOf(
467 serializedParameterizedCommand, PARAMETER_START_CHAR);
468
469 String commandIdEscaped;
470 String serializedParameters;
471 if (lparenPosition is -1) {
472 commandIdEscaped = serializedParameterizedCommand;
473 serializedParameters = null;
474 } else {
475 commandIdEscaped = serializedParameterizedCommand.substring(0,
476 lparenPosition);
477
478 if (serializedParameterizedCommand
479 .charAt(serializedParameterizedCommand.length - 1) !is PARAMETER_END_CHAR) {
480 throw new SerializationException(
481 "Parentheses must be balanced in serialized ParameterizedCommand"); //$NON-NLS-1$
482 }
483
484 serializedParameters = serializedParameterizedCommand.substring(
485 lparenPosition + 1, // skip PARAMETER_START_CHAR
486 serializedParameterizedCommand.length - 1); // skip
487 // PARAMETER_END_CHAR
488 }
489
490 String commandId = unescape(commandIdEscaped);
491 Command command = getCommand(commandId);
492 IParameter[] parameters = command.getParameters();
493 Parameterization[] parameterizations = getParameterizations(
494 serializedParameters, parameters);
495
496 return new ParameterizedCommand(command, parameterizations);
497 }
498
499 /**
500 * Notifies all of the listeners to this manager that the set of defined
501 * command identifiers has changed.
502 *
503 * @param event
504 * The event to send to all of the listeners; must not be
505 * <code>null</code>.
506 */
507 private final void fireCommandManagerChanged(CommandManagerEvent event) {
508 if (event is null) {
509 throw new NullPointerException();
510 }
511
512 Object[] listeners = getListeners();
513 for (int i = 0; i < listeners.length; i++) {
514 ICommandManagerListener listener = cast(ICommandManagerListener) listeners[i];
515 listener.commandManagerChanged(event);
516 }
517 }
518
519 /**
520 * Returns all of the commands known by this manager -- defined and
521 * undefined.
522 *
523 * @return All of the commands; may be empty, but never <code>null</code>.
524 * @since 3.2
525 */
526 public final Command[] getAllCommands() {
527 return arraycast!(Command)( handleObjectsById.values().toArray());
528 }
529
530 /**
531 * Gets the category with the given identifier. If no such category
532 * currently exists, then the category will be created (but be undefined).
533 *
534 * @param categoryId
535 * The identifier to find; must not be <code>null</code>. If
536 * the category is <code>null</code>, then a category suitable
537 * for uncategorized items is defined and returned.
538 * @return The category with the given identifier; this value will never be
539 * <code>null</code>, but it might be undefined.
540 * @see Category
541 */
542 public final Category getCategory(String categoryId) {
543 if (categoryId is null) {
544 return getCategory(AUTOGENERATED_CATEGORY_ID);
545 }
546
547 checkId(categoryId);
548
549 Category category = cast(Category) categoriesById.get(categoryId);
550 if (category is null) {
551 category = new Category(categoryId);
552 categoriesById.put(categoryId, category);
553 category.addCategoryListener(this);
554 }
555
556 return category;
557 }
558
559 /**
560 * Gets the command with the given identifier. If no such command currently
561 * exists, then the command will be created (but will be undefined).
562 *
563 * @param commandId
564 * The identifier to find; must not be <code>null</code> and
565 * must not be zero-length.
566 * @return The command with the given identifier; this value will never be
567 * <code>null</code>, but it might be undefined.
568 * @see Command
569 */
570 public final Command getCommand(String commandId) {
571 checkId(commandId);
572
573 Command command = cast(Command) handleObjectsById.get(commandId);
574 if (command is null) {
575 command = new Command(commandId);
576 handleObjectsById.put(commandId, command);
577 command.addCommandListener(this);
578
579 if (executionListener !is null) {
580 command.addExecutionListener(executionListener);
581 }
582 }
583
584 return command;
585 }
586
587 /**
588 * Returns the categories that are defined.
589 *
590 * @return The defined categories; this value may be empty, but it is never
591 * <code>null</code>.
592 * @since 3.2
593 */
594 public final Category[] getDefinedCategories() {
595 Category[] categories = new Category[definedCategoryIds.size()];
596 Iterator categoryIdItr = definedCategoryIds.iterator();
597 int i = 0;
598 while (categoryIdItr.hasNext()) {
599 String categoryId = stringcast( categoryIdItr.next());
600 categories[i++] = getCategory(categoryId);
601 }
602 return categories;
603 }
604
605 /**
606 * Returns the set of identifiers for those category that are defined.
607 *
608 * @return The set of defined category identifiers; this value may be empty,
609 * but it is never <code>null</code>.
610 */
611 public final Set getDefinedCategoryIds() {
612 return Collections.unmodifiableSet(definedCategoryIds);
613 }
614
615 /**
616 * Returns the set of identifiers for those commands that are defined.
617 *
618 * @return The set of defined command identifiers; this value may be empty,
619 * but it is never <code>null</code>.
620 */
621 public final Set getDefinedCommandIds() {
622 return getDefinedHandleObjectIds();
623 }
624
625 /**
626 * Returns the commands that are defined.
627 *
628 * @return The defined commands; this value may be empty, but it is never
629 * <code>null</code>.
630 * @since 3.2
631 */
632 public final Command[] getDefinedCommands() {
633 return arraycast!(Command)( definedHandleObjects
634 .toArray());
635 }
636
637 /**
638 * Returns the set of identifiers for those parameter types that are
639 * defined.
640 *
641 * @return The set of defined command parameter type identifiers; this value
642 * may be empty, but it is never <code>null</code>.
643 * @since 3.2
644 */
645 public final Set getDefinedParameterTypeIds() {
646 return Collections.unmodifiableSet(definedParameterTypeIds);
647 }
648
649 /**
650 * Returns the command parameter types that are defined.
651 *
652 * @return The defined command parameter types; this value may be empty, but
653 * it is never <code>null</code>.
654 * @since 3.2
655 */
656 public final ParameterType[] getDefinedParameterTypes() {
657 ParameterType[] parameterTypes = new ParameterType[definedParameterTypeIds
658 .size()];
659 Iterator iterator = definedParameterTypeIds.iterator();
660 int i = 0;
661 while (iterator.hasNext()) {
662 String parameterTypeId = stringcast( iterator.next());
663 parameterTypes[i++] = getParameterType(parameterTypeId);
664 }
665 return parameterTypes;
666 }
667
668 /**
669 * Gets the help context identifier for a particular command. The command's
670 * handler is first checked for a help context identifier. If the handler
671 * does not have a help context identifier, then the help context identifier
672 * for the command is returned. If neither has a help context identifier,
673 * then <code>null</code> is returned.
674 *
675 * @param command
676 * The command for which the help context should be retrieved;
677 * must not be <code>null</code>.
678 * @return The help context identifier to use for the given command; may be
679 * <code>null</code>.
680 * @throws NotDefinedException
681 * If the given command is not defined.
682 * @since 3.2
683 */
684 public final String getHelpContextId(Command command) {
685 // Check if the command is defined.
686 if (!command.isDefined()) {
687 throw new NotDefinedException("The command is not defined. " //$NON-NLS-1$
688 ~ command.getId());
689 }
690
691 // Check the handler.
692 IHandler handler = command.getHandler();
693 if (handler !is null) {
694 String helpContextId = stringcast( helpContextIdsByHandler.get( cast(Object) handler) );
695 if (helpContextId !is null) {
696 return helpContextId;
697 }
698 }
699
700 // Simply return whatever the command has as a help context identifier.
701 return command.getHelpContextId();
702 }
703
704 /**
705 * Returns an array of parameterizations for the provided command by
706 * deriving the parameter ids and values from the provided
707 * <code>serializedParameters</code> string.
708 *
709 * @param serializedParameters
710 * a String encoding parameter ids and values; must not be
711 * <code>null</code>.
712 * @param parameters
713 * array of parameters of the command being deserialized; may be
714 * <code>null</code>.
715 * @return an array of parameterizations; may be <code>null</code>.
716 * @throws SerializationException
717 * if there is an error deserializing the parameters
718 * @since 3.2
719 */
720 private final Parameterization[] getParameterizations(
721 String serializedParameters, IParameter[] parameters) {
722
723 if (serializedParameters is null
724 || (serializedParameters.length is 0)) {
725 return null;
726 }
727
728 if ((parameters is null) || (parameters.length is 0)) {
729 return null;
730 }
731
732 ArrayList paramList = new ArrayList();
733
734 int commaPosition; // split off each param by looking for ','
735 do {
736 commaPosition = unescapedIndexOf(serializedParameters, ',');
737
738 String idEqualsValue;
739 if (commaPosition is -1) {
740 // no more parameters after this
741 idEqualsValue = serializedParameters;
742 } else {
743 // take the first parameter...
744 idEqualsValue = serializedParameters
745 .substring(0, commaPosition);
746
747 // ... and put the rest back into serializedParameters
748 serializedParameters = serializedParameters
749 .substring(commaPosition + 1);
750 }
751
752 int equalsPosition = unescapedIndexOf(idEqualsValue, '=');
753
754 String parameterId;
755 String parameterValue;
756 if (equalsPosition is -1) {
757 // missing values are null
758 parameterId = unescape(idEqualsValue);
759 parameterValue = null;
760 } else {
761 parameterId = unescape(idEqualsValue.substring(0,
762 equalsPosition));
763 parameterValue = unescape(idEqualsValue
764 .substring(equalsPosition + 1));
765 }
766
767 for (int i = 0; i < parameters.length; i++) {
768 IParameter parameter = parameters[i];
769 if (parameter.getId().equals(parameterId)) {
770 paramList.add(new Parameterization(parameter,
771 parameterValue));
772 break;
773 }
774 }
775
776 } while (commaPosition !is -1);
777
778 return arraycast!(Parameterization)( paramList
779 .toArray());
780 }
781
782 /**
783 * Gets the command {@link ParameterType} with the given identifier. If no
784 * such command parameter type currently exists, then the command parameter
785 * type will be created (but will be undefined).
786 *
787 * @param parameterTypeId
788 * The identifier to find; must not be <code>null</code> and
789 * must not be zero-length.
790 * @return The {@link ParameterType} with the given identifier; this value
791 * will never be <code>null</code>, but it might be undefined.
792 * @since 3.2
793 */
794 public final ParameterType getParameterType(String parameterTypeId) {
795 checkId(parameterTypeId);
796
797 ParameterType parameterType = cast(ParameterType) parameterTypesById
798 .get(parameterTypeId);
799 if (parameterType is null) {
800 parameterType = new ParameterType(parameterTypeId);
801 parameterTypesById.put(parameterTypeId, parameterType);
802 parameterType.addListener(this);
803 }
804
805 return parameterType;
806 }
807
808 /**
809 * {@inheritDoc}
810 *
811 * @since 3.2
812 */
813 public final void parameterTypeChanged(
814 ParameterTypeEvent parameterTypeEvent) {
815 if (parameterTypeEvent.isDefinedChanged()) {
816 ParameterType parameterType = parameterTypeEvent
817 .getParameterType();
818 String parameterTypeId = parameterType.getId();
819 bool parameterTypeIdAdded = parameterType.isDefined();
820 if (parameterTypeIdAdded) {
821 definedParameterTypeIds.add(parameterTypeId);
822 } else {
823 definedParameterTypeIds.remove(parameterTypeId);
824 }
825
826 fireCommandManagerChanged(new CommandManagerEvent(this,
827 parameterTypeId, parameterTypeIdAdded, true));
828 }
829 }
830
831 /**
832 * Removes a listener from this command manager.
833 *
834 * @param listener
835 * The listener to be removed; must not be <code>null</code>.
836 */
837 public final void removeCommandManagerListener(
838 ICommandManagerListener listener) {
839 removeListenerObject(cast(Object)listener);
840 }
841
842 /**
843 * Removes an execution listener from this command manager.
844 *
845 * @param listener
846 * The listener to be removed; must not be <code>null</code>.
847 */
848 public final void removeExecutionListener(IExecutionListener listener) {
849 if (listener is null) {
850 throw new NullPointerException("Cannot remove a null listener"); //$NON-NLS-1$
851 }
852
853 if (executionListeners is null) {
854 return;
855 }
856
857 executionListeners.remove(cast(Object)listener);
858
859 if (executionListeners.isEmpty()) {
860 executionListeners = null;
861
862 // Remove the execution listener to every command.
863 Iterator commandItr = handleObjectsById.values().iterator();
864 while (commandItr.hasNext()) {
865 Command command = cast(Command) commandItr.next();
866 command.removeExecutionListener(executionListener);
867 }
868 executionListener = null;
869
870 }
871 }
872
873 /**
874 * Block updates all of the handlers for all of the commands. If the handler
875 * is <code>null</code> or the command id does not exist in the map, then
876 * the command becomes unhandled. Otherwise, the handler is set to the
877 * corresponding value in the map.
878 *
879 * @param handlersByCommandId
880 * A map of command identifiers (<code>String</code>) to
881 * handlers (<code>IHandler</code>). This map may be
882 * <code>null</code> if all handlers should be cleared.
883 * Similarly, if the map is empty, then all commands will become
884 * unhandled.
885 */
886 public final void setHandlersByCommandId(Map handlersByCommandId) {
887 // Make that all the reference commands are created.
888 Iterator commandIdItr = handlersByCommandId.keySet().iterator();
889 while (commandIdItr.hasNext()) {
890 getCommand(stringcast(commandIdItr.next()));
891 }
892
893 // Now, set-up the handlers on all of the existing commands.
894 Iterator commandItr = handleObjectsById.values().iterator();
895 while (commandItr.hasNext()) {
896 Command command = cast(Command) commandItr.next();
897 String commandId = command.getId();
898 Object value = handlersByCommandId.get(commandId);
899 if ( cast(IHandler)value ) {
900 command.setHandler(cast(IHandler) value);
901 } else {
902 command.setHandler(null);
903 }
904 }
905 }
906
907 /**
908 * Sets the help context identifier to associate with a particular handler.
909 *
910 * @param handler
911 * The handler with which to register a help context identifier;
912 * must not be <code>null</code>.
913 * @param helpContextId
914 * The help context identifier to register; may be
915 * <code>null</code> if the help context identifier should be
916 * removed.
917 * @since 3.2
918 */
919 public final void setHelpContextId(IHandler handler,
920 String helpContextId) {
921 if (handler is null) {
922 throw new NullPointerException("The handler cannot be null"); //$NON-NLS-1$
923 }
924 if (helpContextId is null) {
925 helpContextIdsByHandler.remove(cast(Object) handler);
926 } else {
927 helpContextIdsByHandler.put(cast(Object) handler, stringcast(helpContextId));
928 }
929 }
930
931 /**
932 * Searches for the index of a <code>char</code> in a <code>String</code>
933 * but disregards characters prefixed with the {@link #ESCAPE_CHAR} escape
934 * character. This is used by {@link #deserialize(String)} and
935 * {@link #getParameterizations(String, IParameter[])} to parse the
936 * serialized parameterized command string.
937 *
938 * @param escapedText
939 * the string to search for the index of <code>ch</code> in
940 * @param ch
941 * a character to search for in <code>escapedText</code>
942 * @return the index of the first unescaped occurrence of the character in
943 * <code>escapedText</code>, or <code>-1</code> if the
944 * character does not occur unescaped.
945 * @see String#indexOf(int)
946 */
947 private final int unescapedIndexOf(String escapedText, char ch) {
948
949 int pos = escapedText.indexOf(ch);
950
951 // first char can't be escaped
952 if (pos is 0) {
953 return pos;
954 }
955
956 while (pos !is -1) {
957 // look back for the escape character
958 if (escapedText.charAt(pos - 1) !is ESCAPE_CHAR) {
959 return pos;
960 }
961
962 // scan for the next instance of ch
963 pos = escapedText.indexOf(ch, pos + 1);
964 }
965
966 return pos;
967
968 }
969 /**
970 * Fires the <code>notEnabled</code> event for
971 * <code>executionListeners</code>.
972 * <p>
973 * <b>Note:</b> This supports bridging actions to the command framework,
974 * and should not be used outside the framework.
975 * </p>
976 *
977 * @param commandId
978 * The command id of the command about to execute, never
979 * <code>null</code>.
980 * @param exception
981 * The exception, never <code>null</code>.
982 * @since 3.4
983 */
984 public void fireNotEnabled(String commandId, NotEnabledException exception) {
985 if (executionListener !is null) {
986 executionListener.notEnabled(commandId, exception);
987 }
988 }
989
990 /**
991 * Fires the <code>notDefined</code> event for
992 * <code>executionListeners</code>.
993 * <p>
994 * <b>Note:</b> This supports bridging actions to the command framework,
995 * and should not be used outside the framework.
996 * </p>
997 *
998 * @param commandId
999 * The command id of the command about to execute, never
1000 * <code>null</code>.
1001 * @param exception
1002 * The exception, never <code>null</code>.
1003 * @since 3.4
1004 */
1005 public void fireNotDefined(String commandId, NotDefinedException exception) {
1006 if (executionListener !is null) {
1007 executionListener.notDefined(commandId, exception);
1008 }
1009 }
1010
1011 /**
1012 * Fires the <code>preExecute</code> event for
1013 * <code>executionListeners</code>.
1014 * <p>
1015 * <b>Note:</b> This supports bridging actions to the command framework,
1016 * and should not be used outside the framework.
1017 * </p>
1018 *
1019 * @param commandId
1020 * The command id of the command about to execute, never
1021 * <code>null</code>.
1022 * @param event
1023 * The event that triggered the command, may be <code>null</code>.
1024 * @since 3.4
1025 */
1026 public void firePreExecute(String commandId, ExecutionEvent event) {
1027 if (executionListener !is null) {
1028 executionListener.preExecute(commandId, event);
1029 }
1030 }
1031
1032 /**
1033 * Fires the <code>postExecuteSuccess</code> event for
1034 * <code>executionListeners</code>.
1035 * <p>
1036 * <b>Note:</b> This supports bridging actions to the command framework,
1037 * and should not be used outside the framework.
1038 * </p>
1039 *
1040 * @param commandId
1041 * The command id of the command executed, never
1042 * <code>null</code>.
1043 * @param returnValue
1044 * The value returned from the command, may be <code>null</code>.
1045 * @since 3.4
1046 */
1047 public void firePostExecuteSuccess(String commandId, Object returnValue) {
1048 if (executionListener !is null) {
1049 executionListener.postExecuteSuccess(commandId, returnValue);
1050 }
1051 }
1052
1053 /**
1054 * Fires the <code>postExecuteFailure</code> event for
1055 * <code>executionListeners</code>.
1056 * <p>
1057 * <b>Note:</b> This supports bridging actions to the command framework,
1058 * and should not be used outside the framework.
1059 * </p>
1060 *
1061 * @param commandId
1062 * The command id of the command executed, never
1063 * <code>null</code>.
1064 * @param exception
1065 * The exception, never <code>null</code>.
1066 * @since 3.4
1067 */
1068 public void firePostExecuteFailure(String commandId,
1069 ExecutionException exception) {
1070 if (executionListener !is null) {
1071 executionListener.postExecuteFailure(commandId, exception);
1072 }
1073 }
1074 }