View Javadoc

1   /*
2    * Copyright 2002-2004 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.springframework.richclient.command;
17  
18  import java.awt.Color;
19  import java.awt.Container;
20  import java.beans.PropertyChangeEvent;
21  import java.beans.PropertyChangeListener;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.NoSuchElementException;
26  
27  import javax.swing.AbstractButton;
28  import javax.swing.Icon;
29  import javax.swing.JComponent;
30  import javax.swing.JMenuItem;
31  import javax.swing.KeyStroke;
32  import javax.swing.SwingUtilities;
33  
34  import org.springframework.beans.factory.BeanNameAware;
35  import org.springframework.beans.factory.InitializingBean;
36  import org.springframework.binding.value.support.AbstractPropertyChangePublisher;
37  import org.springframework.core.style.ToStringCreator;
38  import org.springframework.richclient.application.ApplicationServicesLocator;
39  import org.springframework.richclient.command.config.CommandButtonConfigurer;
40  import org.springframework.richclient.command.config.CommandButtonIconInfo;
41  import org.springframework.richclient.command.config.CommandButtonLabelInfo;
42  import org.springframework.richclient.command.config.CommandFaceDescriptor;
43  import org.springframework.richclient.command.config.CommandFaceDescriptorRegistry;
44  import org.springframework.richclient.command.support.CommandFaceButtonManager;
45  import org.springframework.richclient.core.SecurityControllable;
46  import org.springframework.richclient.factory.ButtonFactory;
47  import org.springframework.richclient.factory.ComponentFactory;
48  import org.springframework.richclient.factory.MenuFactory;
49  import org.springframework.util.Assert;
50  import org.springframework.util.CachingMapDecorator;
51  import org.springframework.util.StringUtils;
52  
53  /**
54   * <p>
55   * Base class for commands. Extend this class by implementing the
56   * {@link #execute()} method.
57   * </p>
58   *
59   * <p>
60   * Most (if not all) commands result in a UI component. Several methods are
61   * provided here to deliver abstractButtons or menuItems. Configuring this
62   * visual aspect of the command is done by a number of
63   * {@link CommandFaceDescriptor}s. One of these will be registered as the
64   * default while others can be used to create a different look by providing a
65   * faceDescriptorId.
66   * </p>
67   *
68   * @see CommandFaceDescriptor
69   *
70   * @author Keith Donald
71   * @author Jan Hoskens
72   *
73   */
74  public abstract class AbstractCommand extends AbstractPropertyChangePublisher implements InitializingBean,
75  		BeanNameAware, GuardedActionCommandExecutor, SecurityControllable {
76  
77  	/** Property used to notify changes in the <em>enabled</em> state. */
78  	public static final String ENABLED_PROPERTY_NAME = "enabled";
79  
80  	/** Property used to notify changes in the <em>visible</em> state. */
81  	public static final String VISIBLE_PROPERTY_NAME = "visible";
82  
83  	private static final String DEFAULT_FACE_DESCRIPTOR_ID = "default";
84  
85  	private String id;
86  
87  	private String defaultFaceDescriptorId = DEFAULT_FACE_DESCRIPTOR_ID;
88  
89  	private boolean enabled = true;
90  
91  	private boolean visible = true;
92  
93  	private boolean authorized = true;
94  
95  	private String securityControllerId = null;
96  
97  	private Map faceButtonManagers;
98  
99  	private CommandServices commandServices;
100 
101 	private CommandFaceDescriptorRegistry faceDescriptorRegistry;
102 
103 	private Boolean oldEnabledState;
104 
105 	private Boolean oldVisibleState;
106 
107 	/**
108 	 * Default constructor. Id can be set by context.
109 	 *
110 	 * @see BeanNameAware
111 	 */
112 	protected AbstractCommand() {
113 		this(null);
114 	}
115 
116 	/**
117 	 * Constructor providing an id for configuration.
118 	 *
119 	 * @param id
120 	 */
121 	protected AbstractCommand(String id) {
122 		super();
123 		setId(id);
124 		// keep track of enable state for buttons
125 		addEnabledListener(new ButtonEnablingListener());
126 		// keep track of visible state for buttons
127 		addPropertyChangeListener(VISIBLE_PROPERTY_NAME, new ButtonVisibleListener());
128 	}
129 
130 	/**
131 	 * Constructor providing id and encodedLabel. A default FaceDescriptor will
132 	 * be created by passing the encodedLabel.
133 	 *
134 	 * @param id
135 	 * @param encodedLabel label to use when creating the default
136 	 * {@link CommandFaceDescriptor}.
137 	 */
138 	protected AbstractCommand(String id, String encodedLabel) {
139 		this(id, new CommandFaceDescriptor(encodedLabel));
140 	}
141 
142 	/**
143 	 * Constructor providing id and a number of parameters to create a default
144 	 * {@link CommandFaceDescriptor}.
145 	 *
146 	 * @param id
147 	 * @param encodedLabel label for the default {@link CommandFaceDescriptor}.
148 	 * @param icon icon for the default {@link CommandFaceDescriptor}.
149 	 * @param caption caption for the default {@link CommandFaceDescriptor}.
150 	 */
151 	protected AbstractCommand(String id, String encodedLabel, Icon icon, String caption) {
152 		this(id, new CommandFaceDescriptor(encodedLabel, icon, caption));
153 	}
154 
155 	/**
156 	 * Constructor providing an id and the default FaceDescriptor.
157 	 *
158 	 * @param id
159 	 * @param faceDescriptor the default FaceDescriptor to use.
160 	 */
161 	protected AbstractCommand(String id, CommandFaceDescriptor faceDescriptor) {
162 		this(id);
163 		if (faceDescriptor != null) {
164 			setFaceDescriptor(faceDescriptor);
165 		}
166 	}
167 
168 	/**
169 	 * Constructor providing an id and a number of FaceDescriptors. No default
170 	 * faceDescriptor is set.
171 	 *
172 	 * @param id
173 	 * @param faceDescriptors a map which contains &lt;faceDescriptorId,
174 	 * faceDescriptor&gt; pairs.
175 	 */
176 	protected AbstractCommand(String id, Map faceDescriptors) {
177 		this(id);
178 		setFaceDescriptors(faceDescriptors);
179 	}
180 
181 	/**
182 	 * @return id of this Command.
183 	 */
184 	public String getId() {
185 		return this.id;
186 	}
187 
188 	/**
189 	 * Set the id. In most cases, this is provided by the constructor or through
190 	 * the beanId provided in the applicationContext.
191 	 *
192 	 * @param id
193 	 */
194 	protected void setId(String id) {
195 		if (!StringUtils.hasText(id)) {
196 			id = null;
197 		}
198 		this.id = id;
199 	}
200 
201 	/**
202 	 * {@inheritDoc}
203 	 */
204 	public void setBeanName(String name) {
205 		if (getId() == null) {
206 			setId(name);
207 		}
208 	}
209 
210 	/**
211 	 * Set the default faceDescriptor to use for this command.
212 	 *
213 	 * @param faceDescriptor the {@link CommandFaceDescriptor} to use as
214 	 * default.
215 	 */
216 	public void setFaceDescriptor(CommandFaceDescriptor faceDescriptor) {
217 		setFaceDescriptor(getDefaultFaceDescriptorId(), faceDescriptor);
218 	}
219 
220 	/**
221 	 * Add an additional {@link CommandFaceDescriptor}.
222 	 *
223 	 * @param faceDescriptorId key to identify and use this faceDescriptor.
224 	 * @param faceDescriptor additional {@link CommandFaceDescriptor}.
225 	 */
226 	public void setFaceDescriptor(String faceDescriptorId, CommandFaceDescriptor faceDescriptor) {
227 		getButtonManager(faceDescriptorId).setFaceDescriptor(faceDescriptor);
228 	}
229 
230 	/**
231 	 * Add a number of {@link CommandFaceDescriptor}s to this Command.
232 	 *
233 	 * @param faceDescriptors a {@link Map} which contains &lt;faceDescriptorId,
234 	 * CommandFaceDescriptor&gt; pairs.
235 	 */
236 	public void setFaceDescriptors(Map faceDescriptors) {
237 		Assert.notNull(faceDescriptors);
238 		Iterator it = faceDescriptors.entrySet().iterator();
239 		while (it.hasNext()) {
240 			Map.Entry entry = (Map.Entry) it.next();
241 			String faceDescriptorId = (String) entry.getKey();
242 			CommandFaceDescriptor faceDescriptor = (CommandFaceDescriptor) entry.getValue();
243 			setFaceDescriptor(faceDescriptorId, faceDescriptor);
244 		}
245 	}
246 
247 	/**
248 	 * Change the default FaceDescriptor.
249 	 *
250 	 * @param defaultFaceDescriptorId the id of the faceDescriptor to be used as
251 	 * default.
252 	 */
253 	public void setDefaultFaceDescriptorId(String defaultFaceDescriptorId) {
254 		this.defaultFaceDescriptorId = defaultFaceDescriptorId;
255 	}
256 
257 	/**
258 	 * Set the {@link CommandFaceDescriptorRegistry} to use when
259 	 * registering/looking up {@link CommandFaceDescriptor}s.
260 	 *
261 	 * @param faceDescriptorRegistry registry to use for the
262 	 * {@link CommandFaceDescriptor}s.
263 	 */
264 	public void setFaceDescriptorRegistry(CommandFaceDescriptorRegistry faceDescriptorRegistry) {
265 		this.faceDescriptorRegistry = faceDescriptorRegistry;
266 	}
267 
268 	/**
269 	 * Set the {@link CommandServices}.
270 	 */
271 	public void setCommandServices(CommandServices services) {
272 		this.commandServices = services;
273 	}
274 
275 	/**
276 	 * Set the provided label on the default {@link CommandFaceDescriptor}.
277 	 *
278 	 * @see CommandFaceDescriptor#setButtonLabelInfo(String)
279 	 */
280 	public void setLabel(String encodedLabel) {
281 		getOrCreateFaceDescriptor().setButtonLabelInfo(encodedLabel);
282 	}
283 
284 	/**
285 	 * Set the provided label on the default {@link CommandFaceDescriptor}.
286 	 *
287 	 * @see CommandFaceDescriptor#setLabelInfo(String)
288 	 */
289 	public void setLabel(CommandButtonLabelInfo label) {
290 		getOrCreateFaceDescriptor().setLabelInfo(label);
291 	}
292 
293 	/**
294 	 * Set the provided description on the default {@link CommandFaceDescriptor}.
295 	 *
296 	 * @see CommandFaceDescriptor#setCaption(String)
297 	 */
298 	public void setCaption(String shortDescription) {
299 		getOrCreateFaceDescriptor().setCaption(shortDescription);
300 	}
301 
302 	/**
303 	 * Set the provided icon on the default {@link CommandFaceDescriptor}.
304 	 *
305 	 * @see CommandFaceDescriptor#setIcon(Icon)
306 	 */
307 	public void setIcon(Icon icon) {
308 		getOrCreateFaceDescriptor().setIcon(icon);
309 	}
310 
311 	/**
312 	 * Set the provided iconInfo on the default {@link CommandFaceDescriptor}.
313 	 *
314 	 * @see CommandFaceDescriptor#setIconInfo(CommandButtonIconInfo)
315 	 */
316 	public void setIconInfo(CommandButtonIconInfo iconInfo) {
317 		getOrCreateFaceDescriptor().setIconInfo(iconInfo);
318 	}
319 	
320 	/**
321 	 * Set the provided foreground colour on the default {@link CommandFaceDescriptor}.
322 	 *
323 	 * @see CommandFaceDescriptor#setForeground(Color)
324 	 */
325 	public void setForeground(Color foreground) {
326 		getOrCreateFaceDescriptor().setForeground(foreground);
327 	}
328 	
329 	/**
330 	 * Set the provided background colour on the default {@link CommandFaceDescriptor}.
331 	 *
332 	 * @see CommandFaceDescriptor#setBackground(Color)
333 	 */	
334 	public void setBackground(Color background) {
335 		getOrCreateFaceDescriptor().setBackground(background);
336 	}
337 
338 	/**
339 	 * Performs initialisation and validation of this instance after its
340 	 * dependencies have been set. If subclasses override this method, they
341 	 * should begin by calling {@code super.afterPropertiesSet()}.
342 	 */
343 	public void afterPropertiesSet() {
344 		if (getId() == null) {
345 			logger.info("Command " + this + " has no set id; note: anonymous commands cannot be used in registries.");
346 		}
347 		if (this instanceof ActionCommand && !isFaceConfigured()) {
348 			logger.warn("The face descriptor property is not yet set for action command '" + getId()
349 					+ "'; command won't render correctly until this is configured");
350 		}
351 	}
352 
353 	/**
354 	 * Returns the defaultFaceDescriptor. Creates one if needed.
355 	 */
356 	private CommandFaceDescriptor getOrCreateFaceDescriptor() {
357 		if (!isFaceConfigured()) {
358 			if (logger.isInfoEnabled()) {
359 				logger.info("Lazily instantiating default face descriptor on behalf of caller to prevent npe; "
360 						+ "command is being configured manually, right?");
361 			}
362 			setFaceDescriptor(new CommandFaceDescriptor());
363 		}
364 		return getFaceDescriptor();
365 	}
366 
367 	/**
368 	 * Returns the default faceDescriptorId.
369 	 */
370 	public String getDefaultFaceDescriptorId() {
371 		if (!StringUtils.hasText(defaultFaceDescriptorId)) {
372 			return DEFAULT_FACE_DESCRIPTOR_ID;
373 		}
374 		return defaultFaceDescriptorId;
375 	}
376 
377 	/**
378 	 * Returns the default faceDescriptor.
379 	 */
380 	protected CommandFaceDescriptor getFaceDescriptor() {
381 		return getDefaultButtonManager().getFaceDescriptor();
382 	}
383 
384 	/**
385 	 * Returns <code>true</code> if this command has a default faceDescriptor.
386 	 */
387 	public boolean isFaceConfigured() {
388 		return getDefaultButtonManager().isFaceConfigured();
389 	}
390 
391 	/**
392 	 * Returns the icon from the default faceDescriptor or <code>null</code>
393 	 * if no faceDescriptor is available.
394 	 */
395 	public Icon getIcon() {
396 		if (isFaceConfigured()) {
397 			return getFaceDescriptor().getIcon();
398 		}
399 		return null;
400 	}
401 
402 	/**
403 	 * Returns the text from the default faceDescriptor or the default text of
404 	 * the {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getText()}.
405 	 */
406 	public String getText() {
407 		if (isFaceConfigured()) {
408 			return getFaceDescriptor().getText();
409 		}
410 		return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getText();
411 	}
412 
413 	/**
414 	 * Returns the mnemonic from the default faceDescriptor or the default
415 	 * mnemonic of the
416 	 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getMnemonic()}.
417 	 */
418 	public int getMnemonic() {
419 		if (isFaceConfigured()) {
420 			return getFaceDescriptor().getMnemonic();
421 		}
422 		return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getMnemonic();
423 	}
424 
425 	/**
426 	 * Returns the mnemonicIndex from the default faceDescriptor or the default
427 	 * mnemonicIndex of the
428 	 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getMnemonicIndex()}.
429 	 */
430 	public int getMnemonicIndex() {
431 		if (isFaceConfigured()) {
432 			return getFaceDescriptor().getMnemonicIndex();
433 		}
434 		return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getMnemonicIndex();
435 	}
436 
437 	/**
438 	 * Returns the accelerator from the default faceDescriptor or the default
439 	 * accelerator of the
440 	 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getAccelerator()}.
441 	 */
442 	public KeyStroke getAccelerator() {
443 		if (isFaceConfigured()) {
444 			return getFaceDescriptor().getAccelerator();
445 		}
446 		return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getAccelerator();
447 	}
448 
449 	/**
450 	 * Returns the {@link CommandFaceDescriptorRegistry} of this
451 	 * {@link AbstractCommand} which holds all face descriptors.
452 	 */
453 	public CommandFaceDescriptorRegistry getFaceDescriptorRegistry() {
454 		return faceDescriptorRegistry;
455 	}
456 
457 	/**
458 	 * Returns the {@link CommandServices} for this {@link AbstractCommand}.
459 	 */
460 	protected CommandServices getCommandServices() {
461 		if (commandServices == null) {
462 			commandServices = (CommandServices) ApplicationServicesLocator.services().getService(CommandServices.class);
463 		}
464 		return this.commandServices;
465 	}
466 
467 	/**
468 	 * Set the Id of the security controller that should manage this object.
469 	 * @param controllerId Id (bean name) of the security controller
470 	 */
471 	public void setSecurityControllerId(String controllerId) {
472 		this.securityControllerId = controllerId;
473 	}
474 
475 	/**
476 	 * Get the id (bean name) of the security controller that should manage this
477 	 * object.
478 	 * @return controller id
479 	 */
480 	public String getSecurityControllerId() {
481 		return securityControllerId;
482 	}
483 
484 	/**
485 	 * Set the authorized state. Setting authorized to false will override any
486 	 * call to {@link #setEnabled(boolean)}. As long as this object is
487 	 * unauthorized, it can not be enabled.
488 	 * @param authorized Pass <code>true</code> if the object is to be
489 	 * authorized
490 	 */
491 	public void setAuthorized(boolean authorized) {
492 		boolean wasAuthorized = isAuthorized();
493 		if (hasChanged(wasAuthorized, authorized)) {
494 			this.authorized = authorized;
495 			firePropertyChange(AUTHORIZED_PROPERTY, wasAuthorized, authorized);
496 			updatedEnabledState();
497 		}
498 	}
499 
500 	/**
501 	 * Returns <code>true</code> if the command is authorized.
502 	 */
503 	public boolean isAuthorized() {
504 		return authorized;
505 	}
506 
507 	/**
508 	 * Returns <code>true</code> if the command is enabled and
509 	 * {@link #isAuthorized()}.
510 	 *
511 	 * @see #isAuthorized()
512 	 */
513 	public boolean isEnabled() {
514 		return enabled && isAuthorized();
515 	}
516 
517 	/**
518 	 * This method is called when any predicate for enabled state has changed.
519 	 * This implementation fires the enabled changed event if the return value
520 	 * of {@link #isEnabled()} has changed.
521 	 * <p>
522 	 * Subclasses which have an additional predicate to enabled state must call
523 	 * this method if the state of the predicate changes.
524 	 */
525 	protected void updatedEnabledState() {
526 		boolean isEnabled = isEnabled();
527 		if (oldEnabledState == null || hasChanged(oldEnabledState.booleanValue(), isEnabled)) {
528 			firePropertyChange(ENABLED_PROPERTY_NAME, oldEnabledState == null ? !isEnabled : oldEnabledState
529 					.booleanValue(), isEnabled);
530 		}
531 		oldEnabledState = Boolean.valueOf(isEnabled);
532 	}
533 
534 	/**
535 	 * Set the enabled state of this command. Note that if we are currently not
536 	 * authorized, then the new value will just be recorded and no change in the
537 	 * current enabled state will be made.
538 	 * @param enabled state
539 	 */
540 	public void setEnabled(boolean enabled) {
541 		if (hasChanged(this.enabled, enabled)) {
542 			this.enabled = enabled;
543 			updatedEnabledState();
544 		}
545 	}
546 
547 	/**
548 	 * Listener to keep track of enabled state. When enable on command changes,
549 	 * each button has to be checked and set.
550 	 */
551 	private class ButtonEnablingListener implements PropertyChangeListener {
552 		public void propertyChange(PropertyChangeEvent evt) {
553 			// We need to keep the buttons in sync with the command, so go
554 			// through the buttons and set Enabled state.
555 			// alternative is to add a listener to the enabled value and change
556 			// buttons in that listener
557 			// NOT redundant
558 			boolean enabled = evt.getNewValue() == Boolean.TRUE;
559 			Iterator it = buttonIterator();
560 			while (it.hasNext()) {
561 				AbstractButton button = (AbstractButton) it.next();
562 				button.setEnabled(enabled);
563 			}
564 		}
565 	}
566 
567 	/**
568 	 * Listener to keep track of visible state. When visible on command changes,
569 	 * each button has to be checked and set.
570 	 */
571 	private class ButtonVisibleListener implements PropertyChangeListener {
572 		public void propertyChange(PropertyChangeEvent evt) {
573 			// We need to keep the buttons in sync with the command, so go
574 			// through the buttons and set visible state.
575 			// alternative is to add a listener to the visible value and change
576 			// buttons in that listener
577 			// NOT redundant
578 			boolean enabled = evt.getNewValue() == Boolean.TRUE;
579 			Iterator it = buttonIterator();
580 			while (it.hasNext()) {
581 				AbstractButton button = (AbstractButton) it.next();
582 				button.setVisible(enabled);
583 			}
584 		}
585 	}
586 
587 	/**
588 	 * {@inheritDoc}
589 	 */
590 	public void addEnabledListener(PropertyChangeListener listener) {
591 		addPropertyChangeListener(ENABLED_PROPERTY_NAME, listener);
592 	}
593 
594 	/**
595 	 * {@inheritDoc}
596 	 */
597 	public void removeEnabledListener(PropertyChangeListener listener) {
598 		removePropertyChangeListener(ENABLED_PROPERTY_NAME, listener);
599 	}
600 
601 	/**
602 	 * <p>
603 	 * Returns an iterator over all buttons in the default
604 	 * {@link CommandFaceButtonManager}.
605 	 * </p>
606 	 * <p>
607 	 * To traverse all buttons of all {@link CommandFaceButtonManager}s see
608 	 * {@link #buttonIterator()}.
609 	 * </p>
610 	 */
611 	protected final Iterator defaultButtonIterator() {
612 		return getDefaultButtonManager().iterator();
613 	}
614 
615 	/**
616 	 * Returns an iterator over <em>all</em> buttons by traversing
617 	 * <em>each</em> {@link CommandFaceButtonManager}.
618 	 */
619 	protected final Iterator buttonIterator() {
620 
621 		if (this.faceButtonManagers == null)
622 			return Collections.EMPTY_SET.iterator();
623 
624 		return new NestedButtonIterator(this.faceButtonManagers.values().iterator());
625 	}
626 
627 	/**
628 	 * Iterator to traverse all buttons in every
629 	 * {@link CommandFaceButtonManager} of this {@link AbstractCommand}.
630 	 */
631 	private static final class NestedButtonIterator implements Iterator {
632 		private final Iterator managerIterator;
633 
634 		private Iterator currentButtonIterator;
635 
636 		private AbstractButton nextButton;
637 
638 		NestedButtonIterator(Iterator it) {
639 			this.managerIterator = it;
640 			preFetchNextButton();
641 		}
642 
643 		public boolean hasNext() {
644 			return nextButton != null;
645 		}
646 
647 		public Object next() {
648 			if (nextButton == null) {
649 				throw new NoSuchElementException();
650 			}
651 			AbstractButton lastButton = nextButton;
652 			preFetchNextButton();
653 			return lastButton;
654 		}
655 
656 		public void remove() {
657 			throw new UnsupportedOperationException("Can't use a button-iterator on AbstractCommand to remove buttons.");
658 		}
659 
660 		private void preFetchNextButton() {
661 			while (this.currentButtonIterator == null || !this.currentButtonIterator.hasNext()) {
662 				if (this.managerIterator.hasNext()) {
663 					CommandFaceButtonManager cfbm = (CommandFaceButtonManager) this.managerIterator.next();
664 					this.currentButtonIterator = cfbm.iterator();
665 				}
666 				else {
667 					this.currentButtonIterator = null;
668 					this.nextButton = null;
669 					return;
670 				}
671 			}
672 
673 			if (this.currentButtonIterator.hasNext())
674 				nextButton = (AbstractButton) this.currentButtonIterator.next();
675 			else
676 				nextButton = null;
677 		}
678 	}
679 
680 	/**
681 	 * Returns <code>true</code> if this command doesn't have an Id.
682 	 */
683 	public boolean isAnonymous() {
684 		return id == null;
685 	}
686 
687 	/**
688 	 * Returns <code>true</code> if the command is visible.
689 	 */
690 	public boolean isVisible() {
691 		return this.visible;
692 	}
693 
694 	/**
695 	 * Set this command visible and update all associated buttons.
696 	 */
697 	public void setVisible(boolean value) {
698 		if (visible != value) {
699 			this.visible = value;
700 			updatedVisibleState();
701 		}
702 	}
703 
704 	/**
705 	 * <p>
706 	 * This method is called when any predicate for visible state has changed.
707 	 * This implementation fires the visible changed event if the return value
708 	 * of {@link #isVisible()} has changed.
709 	 * </p>
710 	 * <p>
711 	 * Subclasses which have an additional predicate to visible state must call
712 	 * this method if the state of the predicate changes.
713 	 * </p>
714 	 */
715 	protected void updatedVisibleState() {
716 		boolean isVisible = isVisible();
717 		if (oldVisibleState == null || hasChanged(oldVisibleState.booleanValue(), isVisible)) {
718 			firePropertyChange(VISIBLE_PROPERTY_NAME, oldVisibleState == null ? !isVisible : oldVisibleState
719 					.booleanValue(), isVisible);
720 		}
721 		oldVisibleState = Boolean.valueOf(isVisible);
722 	}
723 
724 	/**
725 	 * Create a button using the defaults for faceDescriptorId, buttonFactory
726 	 * and buttonConfigurer.
727 	 *
728 	 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
729 	 */
730 	public final AbstractButton createButton() {
731 		return createButton(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer());
732 	}
733 
734 	/**
735 	 * Create a button using the defaults for buttonFactory and
736 	 * buttonConfigurer.
737 	 *
738 	 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
739 	 */
740 	public final AbstractButton createButton(String faceDescriptorId) {
741 		return createButton(faceDescriptorId, getButtonFactory(), getDefaultButtonConfigurer());
742 	}
743 
744 	/**
745 	 * Create a button using the defaults for faceDescriptorId and
746 	 * buttonConfigurer.
747 	 *
748 	 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
749 	 */
750 	public final AbstractButton createButton(ButtonFactory buttonFactory) {
751 		return createButton(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer());
752 	}
753 
754 	/**
755 	 * Create a button using the default buttonConfigurer.
756 	 *
757 	 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
758 	 */
759 	public final AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory) {
760 		return createButton(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer());
761 	}
762 
763 	/**
764 	 * Create a button using the default buttonFactory.
765 	 *
766 	 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
767 	 */
768 	public final AbstractButton createButton(ButtonFactory buttonFactory, CommandButtonConfigurer buttonConfigurer) {
769 		return createButton(getDefaultFaceDescriptorId(), buttonFactory, buttonConfigurer);
770 	}
771 
772 	/**
773 	 * Creates a button using the provided id, factory and configurer.
774 	 *
775 	 * @param faceDescriptorId id of the faceDescriptor used to configure the
776 	 * button.
777 	 * @param buttonFactory factory that delivers the button.
778 	 * @param buttonConfigurer configurer mapping the faceDescriptor on the
779 	 * button.
780 	 * @return a button attached to this command.
781 	 */
782 	public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory,
783 			CommandButtonConfigurer buttonConfigurer) {
784 		AbstractButton button = buttonFactory.createButton();
785 		attach(button, faceDescriptorId, buttonConfigurer);
786 		return button;
787 	}
788 
789 	/**
790 	 * Create a menuItem using the defaults for faceDescriptorId, menuFactory
791 	 * and menuItemButtonConfigurer.
792 	 *
793 	 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
794 	 */
795 	public final JMenuItem createMenuItem() {
796 		return createMenuItem(getDefaultFaceDescriptorId(), getMenuFactory(), getMenuItemButtonConfigurer());
797 	}
798 
799 	/**
800 	 * Create a menuItem using the defaults for menuFactory and
801 	 * menuItemButtonConfigurer.
802 	 *
803 	 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
804 	 */
805 	public final JMenuItem createMenuItem(String faceDescriptorId) {
806 		return createMenuItem(faceDescriptorId, getMenuFactory(), getMenuItemButtonConfigurer());
807 	}
808 
809 	/**
810 	 * Create a menuItem using the defaults for faceDescriptorId and
811 	 * menuItemButtonConfigurer.
812 	 *
813 	 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
814 	 */
815 	public final JMenuItem createMenuItem(MenuFactory menuFactory) {
816 		return createMenuItem(getDefaultFaceDescriptorId(), menuFactory, getMenuItemButtonConfigurer());
817 	}
818 
819 	/**
820 	 * Create a menuItem using the default and menuItemButtonConfigurer.
821 	 *
822 	 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
823 	 */
824 	public final JMenuItem createMenuItem(String faceDescriptorId, MenuFactory menuFactory) {
825 		return createMenuItem(faceDescriptorId, menuFactory, getMenuItemButtonConfigurer());
826 	}
827 
828 	/**
829 	 * Create a menuItem using the default faceDescriptorId.
830 	 *
831 	 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
832 	 */
833 	public final JMenuItem createMenuItem(MenuFactory menuFactory, CommandButtonConfigurer buttonConfigurer) {
834 		return createMenuItem(getDefaultFaceDescriptorId(), menuFactory, buttonConfigurer);
835 	}
836 
837 	/**
838 	 * Create a menuItem using the provided id, factory and configurer.
839 	 *
840 	 * @param faceDescriptorId id of the faceDescriptor used to configure the
841 	 * button.
842 	 * @param menuFactory factory that delivers the menuItem.
843 	 * @param buttonConfigurer configurer mapping the faceDescriptor on the
844 	 * button.
845 	 * @return a menuItem attached to this command.
846 	 */
847 	public JMenuItem createMenuItem(String faceDescriptorId, MenuFactory menuFactory,
848 			CommandButtonConfigurer buttonConfigurer) {
849 		JMenuItem menuItem = menuFactory.createMenuItem();
850 		attach(menuItem, faceDescriptorId, buttonConfigurer);
851 		return menuItem;
852 	}
853 
854 	/**
855 	 * Attach and configure the button to the default faceDescriptor using the
856 	 * default configurer.
857 	 *
858 	 * @see #attach(AbstractButton, String, CommandButtonConfigurer)
859 	 */
860 	public void attach(AbstractButton button) {
861 		attach(button, getDefaultFaceDescriptorId(), getCommandServices().getDefaultButtonConfigurer());
862 	}
863 
864 	/**
865 	 * Attach and configure the button to the default faceDescriptor using the
866 	 * given configurer.
867 	 *
868 	 * @see #attach(AbstractButton, String, CommandButtonConfigurer)
869 	 */
870 	public void attach(AbstractButton button, CommandButtonConfigurer configurer) {
871 		attach(button, getDefaultFaceDescriptorId(), configurer);
872 	}
873 
874 	/**
875 	 * Attach and configure the button to the faceDescriptorId using the configurer.
876 	 *
877 	 * @param button the button to attach and configure.
878 	 * @param faceDescriptorId the id of the faceDescriptor.
879 	 * @param configurer that maps the faceDescriptor on the button.
880 	 */
881 	public void attach(AbstractButton button, String faceDescriptorId, CommandButtonConfigurer configurer) {
882 		getButtonManager(faceDescriptorId).attachAndConfigure(button, configurer);
883 		onButtonAttached(button);
884 	}
885 
886 	/**
887 	 * Additional code to execute when attaching a button.
888 	 *
889 	 * @param button the button that has been attached.
890 	 */
891 	protected void onButtonAttached(AbstractButton button) {
892 		if (logger.isDebugEnabled()) {
893 			logger.debug("Configuring newly attached button for command '" + getId() + "' enabled=" + isEnabled()
894 					+ ", visible=" + isVisible());
895 		}
896 		button.setEnabled(isEnabled());
897 		button.setVisible(isVisible());
898 	}
899 
900 	/**
901 	 * Detach the button from the {@link CommandFaceButtonManager}.
902 	 *
903 	 * @param button the button to detach.
904 	 */
905 	public void detach(AbstractButton button) {
906 		if (getDefaultButtonManager().isAttachedTo(button)) {
907 			getDefaultButtonManager().detach(button);
908 			onButtonDetached();
909 		}
910 	}
911 
912 	/**
913 	 * Returns <code>true</code> if the provided button is attached to the
914 	 * default {@link CommandFaceButtonManager}.
915 	 *
916 	 * @param b the button to check.
917 	 * @return <code>true</code> if b is attached to the default
918 	 * {@link CommandFaceButtonManager}.
919 	 */
920 	public boolean isAttached(AbstractButton b) {
921 		return getDefaultButtonManager().isAttachedTo(b);
922 	}
923 
924 	/**
925 	 * Implement this to add custom code executed when detaching a button.
926 	 */
927 	protected void onButtonDetached() {
928 		// default no implementation, subclasses may override
929 	}
930 
931 	/**
932 	 * Returns the {@link CommandFaceButtonManager} for the default
933 	 * {@link CommandFaceDescriptor}.
934 	 */
935 	private CommandFaceButtonManager getDefaultButtonManager() {
936 		return getButtonManager(getDefaultFaceDescriptorId());
937 	}
938 
939 	/**
940 	 * Returns the {@link CommandFaceButtonManager} for the given
941 	 * faceDescriptorId.
942 	 *
943 	 * @param faceDescriptorId id of the {@link CommandFaceDescriptor}.
944 	 * @return the {@link CommandFaceButtonManager} managing buttons configured
945 	 * with the {@link CommandFaceDescriptor}.
946 	 */
947 	private CommandFaceButtonManager getButtonManager(String faceDescriptorId) {
948 		if (this.faceButtonManagers == null) {
949 			this.faceButtonManagers = new CachingMapDecorator() {
950 				protected Object create(Object key) {
951 					return new CommandFaceButtonManager(AbstractCommand.this, (String) key);
952 				}
953 			};
954 		}
955 		CommandFaceButtonManager m = (CommandFaceButtonManager) this.faceButtonManagers.get(faceDescriptorId);
956 		return m;
957 	}
958 
959 	/**
960 	 * @see CommandServices#getDefaultButtonConfigurer()
961 	 */
962 	protected CommandButtonConfigurer getDefaultButtonConfigurer() {
963 		return getCommandServices().getDefaultButtonConfigurer();
964 	}
965 
966 	/**
967 	 * @see CommandServices#getToolBarButtonConfigurer()
968 	 */
969 	protected CommandButtonConfigurer getToolBarButtonConfigurer() {
970 		return getCommandServices().getToolBarButtonConfigurer();
971 	}
972 
973 	/**
974 	 * @see CommandServices#getToolBarButtonFactory()
975 	 */
976 	protected ButtonFactory getToolBarButtonFactory() {
977 		return getCommandServices().getToolBarButtonFactory();
978 	}
979 
980 	/**
981 	 * @see CommandServices#getMenuItemButtonConfigurer()
982 	 */
983 	protected CommandButtonConfigurer getMenuItemButtonConfigurer() {
984 		return getCommandServices().getMenuItemButtonConfigurer();
985 	}
986 
987 	/**
988 	 * @see CommandServices#getComponentFactory()
989 	 */
990 	protected ComponentFactory getComponentFactory() {
991 		return getCommandServices().getComponentFactory();
992 	}
993 
994 	/**
995 	 * @see CommandServices#getButtonFactory()
996 	 */
997 	protected ButtonFactory getButtonFactory() {
998 		return getCommandServices().getButtonFactory();
999 	}
1000 
1001 	/**
1002 	 * @see CommandServices#getMenuFactory()
1003 	 */
1004 	protected MenuFactory getMenuFactory() {
1005 		return getCommandServices().getMenuFactory();
1006 	}
1007 
1008 	/**
1009 	 * Search for a button representing this command in the provided container
1010 	 * and let it request the focus.
1011 	 *
1012 	 * @param container the container which holds the command button.
1013 	 * @return <code>true</code> if the focus request is likely to succeed.
1014 	 *
1015 	 * @see #getButtonIn(Container)
1016 	 * @see JComponent#requestFocusInWindow()
1017 	 */
1018 	public boolean requestFocusIn(Container container) {
1019 		AbstractButton button = getButtonIn(container);
1020 		if (button != null) {
1021 			return button.requestFocusInWindow();
1022 		}
1023 		return false;
1024 	}
1025 
1026 	/**
1027 	 * Search for the first button of this command that is a child component of
1028 	 * the given container.
1029 	 *
1030 	 * @param container the container to be searched.
1031 	 * @return the {@link AbstractButton} representing this command that is
1032 	 * embedded in the container or <code>null</code> if none was found.
1033 	 */
1034 	public AbstractButton getButtonIn(Container container) {
1035 		Iterator it = buttonIterator();
1036 		while (it.hasNext()) {
1037 			AbstractButton button = (AbstractButton) it.next();
1038 			if (SwingUtilities.isDescendingFrom(button, container)) {
1039 				return button;
1040 			}
1041 		}
1042 		return null;
1043 	}
1044 
1045 	/**
1046 	 * {@inheritDoc}
1047 	 */
1048 	public String toString() {
1049 		return new ToStringCreator(this).append("id", getId()).append("enabled", enabled).append("visible", visible)
1050 				.append("defaultFaceDescriptorId", defaultFaceDescriptorId).toString();
1051 	}
1052 
1053 }