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

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 735224fcc45f
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.Command;
14
15 // import java.io.BufferedWriter;
16 // import java.io.IOException;
17 // import java.io.StringWriter;
18
19 import org.eclipse.core.commands.common.NotDefinedException;
20 import org.eclipse.core.commands.util.Tracing;
21 import org.eclipse.core.internal.commands.util.Util;
22 import org.eclipse.core.runtime.ISafeRunnable;
23 import org.eclipse.core.runtime.ListenerList;
24 import org.eclipse.core.runtime.SafeRunner;
25
26 import org.eclipse.core.commands.IParameter;
27 import org.eclipse.core.commands.IHandlerListener;
28 import org.eclipse.core.commands.NotEnabledException;
29 import org.eclipse.core.commands.NotHandledException;
30 import org.eclipse.core.commands.ExecutionException;
31 import org.eclipse.core.commands.CommandEvent;
32 import org.eclipse.core.commands.State;
33 import org.eclipse.core.commands.Category;
34 import org.eclipse.core.commands.NamedHandleObjectWithState;
35 import org.eclipse.core.commands.ExecutionEvent;
36 import org.eclipse.core.commands.ParameterType;
37 import org.eclipse.core.commands.IExecutionListener;
38 import org.eclipse.core.commands.ICommandListener;
39 import org.eclipse.core.commands.IHandler;
40 import org.eclipse.core.commands.IHandler2;
41 import org.eclipse.core.commands.IObjectWithState;
42 import org.eclipse.core.commands.IExecutionListenerWithChecks;
43 import org.eclipse.core.commands.ITypedParameter;
44 import org.eclipse.core.commands.HandlerEvent;
45
46 import java.lang.all;
47 import tango.io.Stdout;
48
49 /**
50 * <p>
51 * A command is an abstract representation for some semantic behaviour. It is
52 * not the actual implementation of this behaviour, nor is it the visual
53 * appearance of this behaviour in the user interface. Instead, it is a bridge
54 * between the two.
55 * </p>
56 * <p>
57 * The concept of a command is based on the command design pattern. The notable
58 * difference is how the command delegates responsibility for execution. Rather
59 * than allowing concrete subclasses, it uses a handler mechanism (see the
60 * <code>handlers</code> extension point). This provides another level of
61 * indirection.
62 * </p>
63 * <p>
64 * A command will exist in two states: defined and undefined. A command is
65 * defined if it is declared in the XML of a resolved plug-in. If the plug-in is
66 * unloaded or the command is simply not declared, then it is undefined. Trying
67 * to reference an undefined command will succeed, but trying to access any of
68 * its functionality will fail with a <code>NotDefinedException</code>. If
69 * you need to know when a command changes from defined to undefined (or vice
70 * versa), then attach a command listener.
71 * </p>
72 * <p>
73 * Commands are mutable and will change as their definition changes.
74 * </p>
75 *
76 * @since 3.1
77 */
78 public final class Command : NamedHandleObjectWithState,
79 Comparable {
80
81 /**
82 * This flag can be set to <code>true</code> if commands should print
83 * information to <code>System.out</code> when executing.
84 */
85 public static bool DEBUG_COMMAND_EXECUTION = false;
86
87 /**
88 * This flag can be set to <code>true</code> if commands should print
89 * information to <code>System.out</code> when changing handlers.
90 */
91 public static bool DEBUG_HANDLERS = false;
92
93 /**
94 * This flag can be set to a particular command identifier if only that
95 * command should print information to <code>System.out</code> when
96 * changing handlers.
97 */
98 public static String DEBUG_HANDLERS_COMMAND_ID = null;
99
100 /**
101 * The category to which this command belongs. This value should not be
102 * <code>null</code> unless the command is undefined.
103 */
104 private Category category = null;
105
106 /**
107 * A collection of objects listening to the execution of this command. This
108 * collection is <code>null</code> if there are no listeners.
109 */
110 private /+transient+/ ListenerList executionListeners = null;
111
112 /**
113 * The handler currently associated with this command. This value may be
114 * <code>null</code> if there is no handler currently.
115 */
116 private /+transient+/ IHandler handler = null;
117
118 /**
119 * The help context identifier for this command. This can be
120 * <code>null</code> if there is no help currently associated with the
121 * command.
122 *
123 * @since 3.2
124 */
125 private String helpContextId;
126
127 /**
128 * The ordered array of parameters understood by this command. This value
129 * may be <code>null</code> if there are no parameters, or if the command
130 * is undefined. It may also be empty.
131 */
132 private IParameter[] parameters = null;
133
134 /**
135 * The type of the return value of this command. This value may be
136 * <code>null</code> if the command does not declare a return type.
137 *
138 * @since 3.2
139 */
140 private ParameterType returnType = null;
141
142 /**
143 * Our command will listen to the active handler for enablement changes so
144 * that they can be fired from the command itself.
145 *
146 * @since 3.3
147 */
148 private IHandlerListener handlerListener;
149
150 /**
151 * Constructs a new instance of <code>Command</code> based on the given
152 * identifier. When a command is first constructed, it is undefined.
153 * Commands should only be constructed by the <code>CommandManager</code>
154 * to ensure that the identifier remains unique.
155 *
156 * @param id
157 * The identifier for the command. This value must not be
158 * <code>null</code>, and must be unique amongst all commands.
159 */
160 this(String id) {
161 super(id);
162 }
163
164 /**
165 * Adds a listener to this command that will be notified when this command's
166 * state changes.
167 *
168 * @param commandListener
169 * The listener to be added; must not be <code>null</code>.
170 */
171 public final void addCommandListener(ICommandListener commandListener) {
172 if (commandListener is null) {
173 throw new NullPointerException("Cannot add a null command listener"); //$NON-NLS-1$
174 }
175 addListenerObject(cast(Object)commandListener);
176 }
177
178 /**
179 * Adds a listener to this command that will be notified when this command
180 * is about to execute.
181 *
182 * @param executionListener
183 * The listener to be added; must not be <code>null</code>.
184 */
185 public final void addExecutionListener(
186 IExecutionListener executionListener) {
187 if (executionListener is null) {
188 throw new NullPointerException(
189 "Cannot add a null execution listener"); //$NON-NLS-1$
190 }
191
192 if (executionListeners is null) {
193 executionListeners = new ListenerList(ListenerList.IDENTITY);
194 }
195
196 executionListeners.add(cast(Object)executionListener);
197 }
198
199 /**
200 * <p>
201 * Adds a state to this command. This will add this state to the active
202 * handler, if the active handler is an instance of {@link IObjectWithState}.
203 * </p>
204 * <p>
205 * A single instance of {@link State} cannot be registered with multiple
206 * commands. Each command requires its own unique instance.
207 * </p>
208 *
209 * @param id
210 * The identifier of the state to add; must not be
211 * <code>null</code>.
212 * @param state
213 * The state to add; must not be <code>null</code>.
214 * @since 3.2
215 */
216 public override void addState(String id, State state) {
217 super.addState(id, state);
218 state.setId(id);
219 if ( auto h = cast(IObjectWithState)handler) {
220 h.addState(id, state);
221 }
222 }
223
224 /**
225 * Compares this command with another command by comparing each of its
226 * non-transient attributes.
227 *
228 * @param object
229 * The object with which to compare; must be an instance of
230 * <code>Command</code>.
231 * @return A negative integer, zero or a postivie integer, if the object is
232 * greater than, equal to or less than this command.
233 */
234 public final int compareTo(Object object) {
235 Command castedObject = cast(Command) object;
236 int compareTo = Util.compare(category, castedObject.category);
237 if (compareTo is 0) {
238 compareTo = Util.compare(defined, castedObject.defined);
239 if (compareTo is 0) {
240 compareTo = Util.compare(description, castedObject.description);
241 if (compareTo is 0) {
242 compareTo = Util.compare(cast(Object)handler, cast(Object)castedObject.handler);
243 if (compareTo is 0) {
244 compareTo = Util.compare(id, castedObject.id);
245 if (compareTo is 0) {
246 compareTo = Util.compare(name, castedObject.name);
247 if (compareTo is 0) {
248 Object[] left, right;
249 foreach( p; parameters ){
250 left ~= cast(Object)p;
251 }
252 foreach( p; castedObject.parameters ){
253 right ~= cast(Object)p;
254 }
255 compareTo = Util.compare(left,
256 right);
257 }
258 }
259 }
260 }
261 }
262 }
263 return compareTo;
264 }
265
266 /**
267 * <p>
268 * Defines this command by giving it a name, and possibly a description as
269 * well. The defined property automatically becomes <code>true</code>.
270 * </p>
271 * <p>
272 * Notification is sent to all listeners that something has changed.
273 * </p>
274 *
275 * @param name
276 * The name of this command; must not be <code>null</code>.
277 * @param description
278 * The description for this command; may be <code>null</code>.
279 * @param category
280 * The category for this command; must not be <code>null</code>.
281 * @since 3.2
282 */
283 public final void define(String name, String description,
284 Category category) {
285 define(name, description, category, null);
286 }
287
288 /**
289 * <p>
290 * Defines this command by giving it a name, and possibly a description as
291 * well. The defined property automatically becomes <code>true</code>.
292 * </p>
293 * <p>
294 * Notification is sent to all listeners that something has changed.
295 * </p>
296 *
297 * @param name
298 * The name of this command; must not be <code>null</code>.
299 * @param description
300 * The description for this command; may be <code>null</code>.
301 * @param category
302 * The category for this command; must not be <code>null</code>.
303 * @param parameters
304 * The parameters understood by this command. This value may be
305 * either <code>null</code> or empty if the command does not
306 * accept parameters.
307 */
308 public final void define(String name, String description,
309 Category category, IParameter[] parameters) {
310 define(name, description, category, parameters, null);
311 }
312
313 /**
314 * <p>
315 * Defines this command by giving it a name, and possibly a description as
316 * well. The defined property automatically becomes <code>true</code>.
317 * </p>
318 * <p>
319 * Notification is sent to all listeners that something has changed.
320 * </p>
321 *
322 * @param name
323 * The name of this command; must not be <code>null</code>.
324 * @param description
325 * The description for this command; may be <code>null</code>.
326 * @param category
327 * The category for this command; must not be <code>null</code>.
328 * @param parameters
329 * The parameters understood by this command. This value may be
330 * either <code>null</code> or empty if the command does not
331 * accept parameters.
332 * @param returnType
333 * The type of value returned by this command. This value may be
334 * <code>null</code> if the command does not declare a return
335 * type.
336 * @since 3.2
337 */
338 public final void define(String name, String description,
339 Category category, IParameter[] parameters,
340 ParameterType returnType) {
341 define(name, description, category, parameters, returnType, null);
342 }
343
344 /**
345 * <p>
346 * Defines this command by giving it a name, and possibly a description as
347 * well. The defined property automatically becomes <code>true</code>.
348 * </p>
349 * <p>
350 * Notification is sent to all listeners that something has changed.
351 * </p>
352 *
353 * @param name
354 * The name of this command; must not be <code>null</code>.
355 * @param description
356 * The description for this command; may be <code>null</code>.
357 * @param category
358 * The category for this command; must not be <code>null</code>.
359 * @param parameters
360 * The parameters understood by this command. This value may be
361 * either <code>null</code> or empty if the command does not
362 * accept parameters.
363 * @param returnType
364 * The type of value returned by this command. This value may be
365 * <code>null</code> if the command does not declare a return
366 * type.
367 * @param helpContextId
368 * The identifier of the help context to associate with this
369 * command; may be <code>null</code> if this command does not
370 * have any help associated with it.
371 * @since 3.2
372 */
373 public final void define(String name, String description,
374 Category category, IParameter[] parameters,
375 ParameterType returnType, String helpContextId) {
376 if (name is null) {
377 throw new NullPointerException(
378 "The name of a command cannot be null"); //$NON-NLS-1$
379 }
380
381 if (category is null) {
382 throw new NullPointerException(
383 "The category of a command cannot be null"); //$NON-NLS-1$
384 }
385
386 bool definedChanged = !this.defined;
387 this.defined = true;
388
389 bool nameChanged = !Util.equals(this.name, name);
390 this.name = name;
391
392 bool descriptionChanged = !Util.equals(this.description,
393 description);
394 this.description = description;
395
396 bool categoryChanged = !Util.equals(this.category, category);
397 this.category = category;
398
399 Object[] pLeft, pRight;
400 foreach( p; this.parameters ){
401 pLeft ~= cast(Object)p;
402 }
403 foreach( p; parameters ){
404 pRight ~= cast(Object)p;
405 }
406 bool parametersChanged = !Util.equals(pLeft,
407 pRight);
408 this.parameters = parameters;
409
410 bool returnTypeChanged = !Util.equals(this.returnType,
411 returnType);
412 this.returnType = returnType;
413
414 bool helpContextIdChanged = !Util.equals(this.helpContextId,
415 helpContextId);
416 this.helpContextId = helpContextId;
417
418 fireCommandChanged(new CommandEvent(this, categoryChanged,
419 definedChanged, descriptionChanged, false, nameChanged,
420 parametersChanged, returnTypeChanged, helpContextIdChanged));
421 }
422
423 /**
424 * Executes this command by delegating to the current handler, if any. If
425 * the debugging flag is set, then this method prints information about
426 * which handler is selected for performing this command. This method will
427 * succeed regardless of whether the command is enabled or defined. It is
428 * generally preferred to call {@link #executeWithChecks(ExecutionEvent)}.
429 *
430 * @param event
431 * An event containing all the information about the current
432 * state of the application; must not be <code>null</code>.
433 * @return The result of the execution; may be <code>null</code>. This
434 * result will be available to the client executing the command, and
435 * execution listeners.
436 * @throws ExecutionException
437 * If the handler has problems executing this command.
438 * @throws NotHandledException
439 * If there is no handler.
440 * @deprecated Please use {@link #executeWithChecks(ExecutionEvent)}
441 * instead.
442 */
443 public final Object execute(ExecutionEvent event) {
444 firePreExecute(event);
445 IHandler handler = this.handler;
446
447 // Perform the execution, if there is a handler.
448 if ((handler !is null) && (handler.isHandled())) {
449 try {
450 Object returnValue = handler.execute(event);
451 firePostExecuteSuccess(returnValue);
452 return returnValue;
453 } catch (ExecutionException e) {
454 firePostExecuteFailure(e);
455 throw e;
456 }
457 }
458
459 NotHandledException e = new NotHandledException(
460 "There is no handler to execute. " ~ getId()); //$NON-NLS-1$
461 fireNotHandled(e);
462 throw e;
463 }
464
465 /**
466 * Executes this command by delegating to the current handler, if any. If
467 * the debugging flag is set, then this method prints information about
468 * which handler is selected for performing this command. This does checks
469 * to see if the command is enabled and defined. If it is not both enabled
470 * and defined, then the execution listeners will be notified and an
471 * exception thrown.
472 *
473 * @param event
474 * An event containing all the information about the current
475 * state of the application; must not be <code>null</code>.
476 * @return The result of the execution; may be <code>null</code>. This
477 * result will be available to the client executing the command, and
478 * execution listeners.
479 * @throws ExecutionException
480 * If the handler has problems executing this command.
481 * @throws NotDefinedException
482 * If the command you are trying to execute is not defined.
483 * @throws NotEnabledException
484 * If the command you are trying to execute is not enabled.
485 * @throws NotHandledException
486 * If there is no handler.
487 * @since 3.2
488 */
489 public final Object executeWithChecks(ExecutionEvent event) {
490 firePreExecute(event);
491 IHandler handler = this.handler;
492
493 if (!isDefined()) {
494 NotDefinedException exception = new NotDefinedException(
495 "Trying to execute a command that is not defined. " //$NON-NLS-1$
496 ~ getId());
497 fireNotDefined(exception);
498 throw exception;
499 }
500
501 // Perform the execution, if there is a handler.
502 if ((handler !is null) && (handler.isHandled())) {
503 setEnabled(event.getApplicationContext());
504 if (!isEnabled()) {
505 NotEnabledException exception = new NotEnabledException(
506 "Trying to execute the disabled command " ~ getId()); //$NON-NLS-1$
507 fireNotEnabled(exception);
508 throw exception;
509 }
510
511 try {
512 Object returnValue = handler.execute(event);
513 firePostExecuteSuccess(returnValue);
514 return returnValue;
515 } catch (ExecutionException e) {
516 firePostExecuteFailure(e);
517 throw e;
518 }
519 }
520
521 NotHandledException e = new NotHandledException(
522 "There is no handler to execute for command " ~ getId()); //$NON-NLS-1$
523 fireNotHandled(e);
524 throw e;
525 }
526
527 /**
528 * Notifies the listeners for this command that it has changed in some way.
529 *
530 * @param commandEvent
531 * The event to send to all of the listener; must not be
532 * <code>null</code>.
533 */
534 private final void fireCommandChanged(CommandEvent commandEvent) {
535 if (commandEvent is null) {
536 throw new NullPointerException("Cannot fire a null event"); //$NON-NLS-1$
537 }
538
539 Object[] listeners = getListeners();
540 for (int i = 0; i < listeners.length; i++) {
541 ICommandListener listener = cast(ICommandListener) listeners[i];
542 SafeRunner.run(new class(listener) ISafeRunnable {
543 ICommandListener listener_;
544 this(ICommandListener a){ this.listener_ = a; }
545 public void handleException(Exception exception) {
546 }
547
548 public void run() {
549 listener_.commandChanged(commandEvent);
550 }
551 });
552 }
553 }
554
555 /**
556 * Notifies the execution listeners for this command that an attempt to
557 * execute has failed because the command is not defined.
558 *
559 * @param e
560 * The exception that is about to be thrown; never
561 * <code>null</code>.
562 * @since 3.2
563 */
564 private final void fireNotDefined(NotDefinedException e) {
565 // Debugging output
566 if (DEBUG_COMMAND_EXECUTION) {
567 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
568 ~ "not defined: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
569 }
570
571 if (executionListeners !is null) {
572 Object[] listeners = executionListeners.getListeners();
573 for (int i = 0; i < listeners.length; i++) {
574 Object object = listeners[i];
575 if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
576 listener.notDefined(getId(), e);
577 }
578 }
579 }
580 }
581
582 /**
583 * Notifies the execution listeners for this command that an attempt to
584 * execute has failed because there is no handler.
585 *
586 * @param e
587 * The exception that is about to be thrown; never
588 * <code>null</code>.
589 * @since 3.2
590 */
591 private final void fireNotEnabled(NotEnabledException e) {
592 // Debugging output
593 if (DEBUG_COMMAND_EXECUTION) {
594 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
595 ~ "not enabled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
596 }
597
598 if (executionListeners !is null) {
599 Object[] listeners = executionListeners.getListeners();
600 for (int i = 0; i < listeners.length; i++) {
601 Object object = listeners[i];
602 if ( auto listener = cast(IExecutionListenerWithChecks)object ) {
603 listener.notEnabled(getId(), e);
604 }
605 }
606 }
607 }
608
609 /**
610 * Notifies the execution listeners for this command that an attempt to
611 * execute has failed because there is no handler.
612 *
613 * @param e
614 * The exception that is about to be thrown; never
615 * <code>null</code>.
616 */
617 private final void fireNotHandled(NotHandledException e) {
618 // Debugging output
619 if (DEBUG_COMMAND_EXECUTION) {
620 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
621 ~ "not handled: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
622 }
623
624 if (executionListeners !is null) {
625 Object[] listeners = executionListeners.getListeners();
626 for (int i = 0; i < listeners.length; i++) {
627 IExecutionListener listener = cast(IExecutionListener) listeners[i];
628 listener.notHandled(getId(), e);
629 }
630 }
631 }
632
633 /**
634 * Notifies the execution listeners for this command that an attempt to
635 * execute has failed during the execution.
636 *
637 * @param e
638 * The exception that has been thrown; never <code>null</code>.
639 * After this method completes, the exception will be thrown
640 * again.
641 */
642 private final void firePostExecuteFailure(ExecutionException e) {
643 // Debugging output
644 if (DEBUG_COMMAND_EXECUTION) {
645 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
646 ~ "failure: id=" ~ getId() ~ "; exception=" ~ e.toString ); //$NON-NLS-1$ //$NON-NLS-2$
647 }
648
649 if (executionListeners !is null) {
650 Object[] listeners = executionListeners.getListeners();
651 for (int i = 0; i < listeners.length; i++) {
652 IExecutionListener listener = cast(IExecutionListener) listeners[i];
653 listener.postExecuteFailure(getId(), e);
654 }
655 }
656 }
657
658 /**
659 * Notifies the execution listeners for this command that an execution has
660 * completed successfully.
661 *
662 * @param returnValue
663 * The return value from the command; may be <code>null</code>.
664 */
665 private final void firePostExecuteSuccess(Object returnValue) {
666 // Debugging output
667 if (DEBUG_COMMAND_EXECUTION) {
668 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
669 ~ "success: id=" ~ getId() ~ "; returnValue=" //$NON-NLS-1$ //$NON-NLS-2$
670 ~ returnValue.toString );
671 }
672
673 if (executionListeners !is null) {
674 Object[] listeners = executionListeners.getListeners();
675 for (int i = 0; i < listeners.length; i++) {
676 IExecutionListener listener = cast(IExecutionListener) listeners[i];
677 listener.postExecuteSuccess(getId(), returnValue);
678 }
679 }
680 }
681
682 /**
683 * Notifies the execution listeners for this command that an attempt to
684 * execute is about to start.
685 *
686 * @param event
687 * The execution event that will be used; never <code>null</code>.
688 */
689 private final void firePreExecute(ExecutionEvent event) {
690 // Debugging output
691 if (DEBUG_COMMAND_EXECUTION) {
692 Tracing.printTrace("COMMANDS", "execute" ~ Tracing.SEPARATOR //$NON-NLS-1$ //$NON-NLS-2$
693 ~ "starting: id=" ~ getId() ~ "; event=" ~ event.toString ); //$NON-NLS-1$ //$NON-NLS-2$
694 }
695
696 if (executionListeners !is null) {
697 Object[] listeners = executionListeners.getListeners();
698 for (int i = 0; i < listeners.length; i++) {
699 IExecutionListener listener = cast(IExecutionListener) listeners[i];
700 listener.preExecute(getId(), event);
701 }
702 }
703 }
704
705 /**
706 * Returns the category for this command.
707 *
708 * @return The category for this command; never <code>null</code>.
709 * @throws NotDefinedException
710 * If the handle is not currently defined.
711 */
712 public final Category getCategory() {
713 if (!isDefined()) {
714 throw new NotDefinedException(
715 "Cannot get the category from an undefined command. " //$NON-NLS-1$
716 ~ id);
717 }
718
719 return category;
720 }
721
722 /**
723 * Returns the current handler for this command. This is used by the command
724 * manager for determining the appropriate help context identifiers and by
725 * the command service to allow handlers to update elements.
726 * <p>
727 * This value can change at any time and should never be cached.
728 * </p>
729 *
730 * @return The current handler for this command; may be <code>null</code>.
731 * @since 3.3
732 */
733 public final IHandler getHandler() {
734 return handler;
735 }
736
737 /**
738 * Returns the help context identifier associated with this command. This
739 * method should not be called by clients. Clients should use
740 * {@link CommandManager#getHelpContextId(Command)} instead.
741 *
742 * @return The help context identifier for this command; may be
743 * <code>null</code> if there is none.
744 * @since 3.2
745 */
746 final String getHelpContextId() {
747 return helpContextId;
748 }
749
750 /**
751 * Returns the parameter with the provided id or <code>null</code> if this
752 * command does not have a parameter with the id.
753 *
754 * @param parameterId
755 * The id of the parameter to retrieve.
756 * @return The parameter with the provided id or <code>null</code> if this
757 * command does not have a parameter with the id.
758 * @throws NotDefinedException
759 * If the handle is not currently defined.
760 * @since 3.2
761 */
762 public final IParameter getParameter(String parameterId) {
763 if (!isDefined()) {
764 throw new NotDefinedException(
765 "Cannot get a parameter from an undefined command. " //$NON-NLS-1$
766 ~ id);
767 }
768
769 if (parameters is null) {
770 return null;
771 }
772
773 for (int i = 0; i < parameters.length; i++) {
774 IParameter parameter = parameters[i];
775 if (parameter.getId().equals(parameterId)) {
776 return parameter;
777 }
778 }
779
780 return null;
781 }
782
783 /**
784 * Returns the parameters for this command. This call triggers provides a
785 * copy of the array, so excessive calls to this method should be avoided.
786 *
787 * @return The parameters for this command. This value might be
788 * <code>null</code>, if the command has no parameters.
789 * @throws NotDefinedException
790 * If the handle is not currently defined.
791 */
792 public final IParameter[] getParameters() {
793 if (!isDefined()) {
794 throw new NotDefinedException(
795 "Cannot get the parameters from an undefined command. " //$NON-NLS-1$
796 ~ id);
797 }
798
799 if ((parameters is null) || (parameters.length is 0)) {
800 return null;
801 }
802
803 IParameter[] returnValue = new IParameter[parameters.length];
804 SimpleType!(IParameter).arraycopy(parameters, 0, returnValue, 0, parameters.length);
805 return returnValue;
806 }
807
808 /**
809 * Returns the {@link ParameterType} for the parameter with the provided id
810 * or <code>null</code> if this command does not have a parameter type
811 * with the id.
812 *
813 * @param parameterId
814 * The id of the parameter to retrieve the {@link ParameterType}
815 * of.
816 * @return The {@link ParameterType} for the parameter with the provided id
817 * or <code>null</code> if this command does not have a parameter
818 * type with the provided id.
819 * @throws NotDefinedException
820 * If the handle is not currently defined.
821 * @since 3.2
822 */
823 public final ParameterType getParameterType(String parameterId) {
824 IParameter parameter = getParameter(parameterId);
825 if ( auto parameterWithType = cast(ITypedParameter)parameter ) {
826 return parameterWithType.getParameterType();
827 }
828 return null;
829 }
830
831 /**
832 * Returns the {@link ParameterType} for the return value of this command or
833 * <code>null</code> if this command does not declare a return value
834 * parameter type.
835 *
836 * @return The {@link ParameterType} for the return value of this command or
837 * <code>null</code> if this command does not declare a return
838 * value parameter type.
839 * @throws NotDefinedException
840 * If the handle is not currently defined.
841 * @since 3.2
842 */
843 public final ParameterType getReturnType() {
844 if (!isDefined()) {
845 throw new NotDefinedException(
846 "Cannot get the return type of an undefined command. " //$NON-NLS-1$
847 ~ id);
848 }
849
850 return returnType;
851 }
852
853 /**
854 * Returns whether this command has a handler, and whether this handler is
855 * also handled and enabled.
856 *
857 * @return <code>true</code> if the command is handled; <code>false</code>
858 * otherwise.
859 */
860 public final bool isEnabled() {
861 if (handler is null) {
862 return false;
863 }
864
865 try {
866 return handler.isEnabled();
867 } catch (Exception e) {
868 if (DEBUG_HANDLERS) {
869 // since this has the ability to generate megs of logs, only
870 // provide information if tracing
871 Tracing.printTrace("HANDLERS", "Handler " ~ (cast(Object)handler).toString() ~ " for " //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
872 ~ id ~ " threw unexpected exception"); //$NON-NLS-1$
873 ExceptionPrintStackTrace( e, Stdout );
874 }
875 }
876 return false;
877 }
878
879 /**
880 * Called be the framework to allow the handler to update its enabled state.
881 *
882 * @param evaluationContext
883 * the state to evaluate against. May be <code>null</code>
884 * which indicates that the handler can query whatever model that
885 * is necessary. This context must not be cached.
886 * @since 3.4
887 */
888 public void setEnabled(Object evaluationContext) {
889 if (null !is cast(IHandler2)handler ) {
890 (cast(IHandler2) handler).setEnabled(evaluationContext);
891 }
892 }
893
894 /**
895 * Returns whether this command has a handler, and whether this handler is
896 * also handled.
897 *
898 * @return <code>true</code> if the command is handled; <code>false</code>
899 * otherwise.
900 */
901 public final bool isHandled() {
902 if (handler is null) {
903 return false;
904 }
905
906 return handler.isHandled();
907 }
908
909 /**
910 * Removes a listener from this command.
911 *
912 * @param commandListener
913 * The listener to be removed; must not be <code>null</code>.
914 *
915 */
916 public final void removeCommandListener(
917 ICommandListener commandListener) {
918 if (commandListener is null) {
919 throw new NullPointerException(
920 "Cannot remove a null command listener"); //$NON-NLS-1$
921 }
922
923 removeListenerObject(cast(Object)commandListener);
924 }
925
926 /**
927 * Removes a listener from this command.
928 *
929 * @param executionListener
930 * The listener to be removed; must not be <code>null</code>.
931 *
932 */
933 public final void removeExecutionListener(
934 IExecutionListener executionListener) {
935 if (executionListener is null) {
936 throw new NullPointerException(
937 "Cannot remove a null execution listener"); //$NON-NLS-1$
938 }
939
940 if (executionListeners !is null) {
941 executionListeners.remove(cast(Object)executionListener);
942 if (executionListeners.isEmpty()) {
943 executionListeners = null;
944 }
945 }
946 }
947
948 /**
949 * <p>
950 * Removes a state from this command. This will remove the state from the
951 * active handler, if the active handler is an instance of
952 * {@link IObjectWithState}.
953 * </p>
954 *
955 * @param stateId
956 * The identifier of the state to remove; must not be
957 * <code>null</code>.
958 * @since 3.2
959 */
960 public override void removeState(String stateId) {
961 if ( auto h = cast(IObjectWithState)handler ) {
962 h.removeState(stateId);
963 }
964 super.removeState(stateId);
965 }
966
967 /**
968 * Changes the handler for this command. This will remove all the state from
969 * the currently active handler (if any), and add it to <code>handler</code>.
970 * If debugging is turned on, then this will also print information about
971 * the change to <code>System.out</code>.
972 *
973 * @param handler
974 * The new handler; may be <code>null</code> if none.
975 * @return <code>true</code> if the handler changed; <code>false</code>
976 * otherwise.
977 */
978 public final bool setHandler(IHandler handler) {
979 if (Util.equals(cast(Object)handler, cast(Object)this.handler)) {
980 return false;
981 }
982
983 // Swap the state around.
984 String[] stateIds = getStateIds();
985 if (stateIds !is null) {
986 for (int i = 0; i < stateIds.length; i++) {
987 String stateId = stateIds[i];
988 if ( auto h = cast(IObjectWithState)this.handler ) {
989 h.removeState(stateId);
990 }
991 if ( auto h = cast(IObjectWithState)handler ) {
992 State stateToAdd = getState(stateId);
993 h.addState(stateId, stateToAdd);
994 }
995 }
996 }
997
998 bool enabled = isEnabled();
999 if (this.handler !is null) {
1000 this.handler.removeHandlerListener(getHandlerListener());
1001 }
1002
1003 // Update the handler, and flush the string representation.
1004 this.handler = handler;
1005 if (this.handler !is null) {
1006 this.handler.addHandlerListener(getHandlerListener());
1007 }
1008 string = null;
1009
1010 // Debugging output
1011 if ((DEBUG_HANDLERS)
1012 && ((DEBUG_HANDLERS_COMMAND_ID is null) || (DEBUG_HANDLERS_COMMAND_ID
1013 .equals(id)))) {
1014 StringBuffer buffer = new StringBuffer("Command('"); //$NON-NLS-1$
1015 buffer.append(id);
1016 buffer.append("') has changed to "); //$NON-NLS-1$
1017 if (handler is null) {
1018 buffer.append("no handler"); //$NON-NLS-1$
1019 } else {
1020 buffer.append('\'');
1021 buffer.append(( cast(Object)handler).toString);
1022 buffer.append("' as its handler"); //$NON-NLS-1$
1023 }
1024 Tracing.printTrace("HANDLERS", buffer.toString()); //$NON-NLS-1$
1025 }
1026
1027 // Send notification
1028 fireCommandChanged(new CommandEvent(this, false, false, false, true,
1029 false, false, false, false, enabled !is isEnabled()));
1030
1031 return true;
1032 }
1033
1034 /**
1035 * @return the handler listener
1036 */
1037 private IHandlerListener getHandlerListener() {
1038 if (handlerListener is null) {
1039 handlerListener = new class IHandlerListener {
1040 public void handlerChanged(HandlerEvent handlerEvent) {
1041 bool enabledChanged = handlerEvent.isEnabledChanged();
1042 bool handledChanged = handlerEvent.isHandledChanged();
1043 fireCommandChanged(new CommandEvent(this.outer, false,
1044 false, false, handledChanged, false, false, false,
1045 false, enabledChanged));
1046 }
1047 };
1048 }
1049 return handlerListener;
1050 }
1051
1052 /**
1053 * The string representation of this command -- for debugging purposes only.
1054 * This string should not be shown to an end user.
1055 *
1056 * @return The string representation; never <code>null</code>.
1057 */
1058 public override final String toString() {
1059 if (string is null) {
1060 String parms;
1061 foreach( p; parameters ){
1062 parms ~= "{"~(cast(Object)p).toString~"}";
1063 }
1064 string = Format("Command({},{},\n\t\t{},\n\t\t{},\n\t\t{},\n\t\t{},{},{})",
1065 id,
1066 name is null ? "":name,
1067 description is null?"":description,
1068 category is null?"":category.toString(),
1069 handler is null?"": (cast(Object)handler).toString(),
1070 parms,
1071 returnType is null?"":returnType.toString(),
1072 defined
1073 );
1074 }
1075 return string;
1076 }
1077
1078 /**
1079 * Makes this command become undefined. This has the side effect of changing
1080 * the name and description to <code>null</code>. This also removes all
1081 * state and disposes of it. Notification is sent to all listeners.
1082 */
1083 public override final void undefine() {
1084 bool enabledChanged = isEnabled();
1085
1086 string = null;
1087
1088 bool definedChanged = defined;
1089 defined = false;
1090
1091 bool nameChanged = name !is null;
1092 name = null;
1093
1094 bool descriptionChanged = description !is null;
1095 description = null;
1096
1097 bool categoryChanged = category !is null;
1098 category = null;
1099
1100 bool parametersChanged = parameters !is null;
1101 parameters = null;
1102
1103 bool returnTypeChanged = returnType !is null;
1104 returnType = null;
1105
1106 String[] stateIds = getStateIds();
1107 if (stateIds !is null) {
1108 if ( auto handlerWithState = cast(IObjectWithState)handler ) {
1109 for (int i = 0; i < stateIds.length; i++) {
1110 String stateId = stateIds[i];
1111 handlerWithState.removeState(stateId);
1112
1113 State state = getState(stateId);
1114 removeState(stateId);
1115 state.dispose();
1116 }
1117 } else {
1118 for (int i = 0; i < stateIds.length; i++) {
1119 String stateId = stateIds[i];
1120 State state = getState(stateId);
1121 removeState(stateId);
1122 state.dispose();
1123 }
1124 }
1125 }
1126
1127 fireCommandChanged(new CommandEvent(this, categoryChanged,
1128 definedChanged, descriptionChanged, false, nameChanged,
1129 parametersChanged, returnTypeChanged, false, enabledChanged));
1130 }
1131 }