001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.command;
017    
018    import java.awt.Color;
019    import java.awt.Container;
020    import java.beans.PropertyChangeEvent;
021    import java.beans.PropertyChangeListener;
022    import java.util.Collections;
023    import java.util.Iterator;
024    import java.util.Map;
025    import java.util.NoSuchElementException;
026    
027    import javax.swing.AbstractButton;
028    import javax.swing.Icon;
029    import javax.swing.JComponent;
030    import javax.swing.JMenuItem;
031    import javax.swing.KeyStroke;
032    import javax.swing.SwingUtilities;
033    
034    import org.springframework.beans.factory.BeanNameAware;
035    import org.springframework.beans.factory.InitializingBean;
036    import org.springframework.binding.value.support.AbstractPropertyChangePublisher;
037    import org.springframework.core.style.ToStringCreator;
038    import org.springframework.richclient.application.ApplicationServicesLocator;
039    import org.springframework.richclient.command.config.CommandButtonConfigurer;
040    import org.springframework.richclient.command.config.CommandButtonIconInfo;
041    import org.springframework.richclient.command.config.CommandButtonLabelInfo;
042    import org.springframework.richclient.command.config.CommandFaceDescriptor;
043    import org.springframework.richclient.command.config.CommandFaceDescriptorRegistry;
044    import org.springframework.richclient.command.support.CommandFaceButtonManager;
045    import org.springframework.richclient.core.SecurityControllable;
046    import org.springframework.richclient.factory.ButtonFactory;
047    import org.springframework.richclient.factory.ComponentFactory;
048    import org.springframework.richclient.factory.MenuFactory;
049    import org.springframework.util.Assert;
050    import org.springframework.util.CachingMapDecorator;
051    import org.springframework.util.StringUtils;
052    
053    /**
054     * <p>
055     * Base class for commands. Extend this class by implementing the
056     * {@link #execute()} method.
057     * </p>
058     *
059     * <p>
060     * Most (if not all) commands result in a UI component. Several methods are
061     * provided here to deliver abstractButtons or menuItems. Configuring this
062     * visual aspect of the command is done by a number of
063     * {@link CommandFaceDescriptor}s. One of these will be registered as the
064     * default while others can be used to create a different look by providing a
065     * faceDescriptorId.
066     * </p>
067     *
068     * @see CommandFaceDescriptor
069     *
070     * @author Keith Donald
071     * @author Jan Hoskens
072     *
073     */
074    public abstract class AbstractCommand extends AbstractPropertyChangePublisher implements InitializingBean,
075                    BeanNameAware, GuardedActionCommandExecutor, SecurityControllable {
076    
077            /** Property used to notify changes in the <em>enabled</em> state. */
078            public static final String ENABLED_PROPERTY_NAME = "enabled";
079    
080            /** Property used to notify changes in the <em>visible</em> state. */
081            public static final String VISIBLE_PROPERTY_NAME = "visible";
082    
083            private static final String DEFAULT_FACE_DESCRIPTOR_ID = "default";
084    
085            private String id;
086    
087            private String defaultFaceDescriptorId = DEFAULT_FACE_DESCRIPTOR_ID;
088    
089            private boolean enabled = true;
090    
091            private boolean visible = true;
092    
093            private boolean authorized = true;
094    
095            private String securityControllerId = null;
096    
097            private Map faceButtonManagers;
098    
099            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    }