001    /*
002     * Copyright 2002-2004 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of 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,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.command;
017    
018    import java.beans.PropertyChangeEvent;
019    import java.beans.PropertyChangeListener;
020    
021    import org.springframework.util.ObjectUtils;
022    
023    /**
024     * An {@link ActionCommand} that delegates to an internal {@link ActionCommandExecutor}.
025     * The executor can be provided at construction but can also be provided 
026     * programmatically at runtime and replaced during program execution. This enables a shared
027     * global command feature whereby a single command is specified that can perform different 
028     * actions depending on the currently active context. 
029     * 
030     * @author Keith Donald
031     */
032    public class TargetableActionCommand extends ActionCommand {
033            
034        private ActionCommandExecutor commandExecutor;
035    
036        private PropertyChangeListener guardRelay;
037    
038        /**
039         * Creates a new uninitialized {@code TargetableActionCommand}. If defined
040         * in a Spring bean factory, the name of the bean will become the id of this instance.
041         */
042        public TargetableActionCommand() {
043            this(null);
044        }
045    
046        /**
047         * Creates a new {@code TargetableActionCommand} with the given identifier. 
048         * The instance will be initialized in a disabled state.
049         *
050         * @param commandId The identifier for this instance.
051         */
052        public TargetableActionCommand(String commandId) {
053            this(commandId, null);
054        }
055    
056        /**
057         * Creates a new {@code TargetableActionCommand} with the given identifier and initial 
058         * executor. The instance will be initialized in a disabled state.
059         *
060         * @param commandId The identifier for this instance. 
061         * @param commandExecutor The initial command executor. 
062         */
063        public TargetableActionCommand(String commandId, ActionCommandExecutor commandExecutor) {
064            super(commandId);
065            setEnabled(false);
066            setCommandExecutor(commandExecutor);
067        }
068    
069        /**
070         * Attaches the given executor to this command instance, detaching the current executor in the process.
071         *
072         * @param commandExecutor The executor to be attached. May be null, in which case this command 
073         * will be disabled.
074         */
075        public void setCommandExecutor(ActionCommandExecutor commandExecutor) {
076            if (ObjectUtils.nullSafeEquals(this.commandExecutor, commandExecutor)) {
077                return;
078            }
079            if (commandExecutor == null) {
080                detachCommandExecutor();
081            }
082            else {
083                if (this.commandExecutor instanceof GuardedActionCommandExecutor) {
084                    unsubscribeFromGuardedCommandDelegate();
085                }
086                this.commandExecutor = commandExecutor;
087                attachCommandExecutor();
088            }
089        }
090    
091        /**
092         * Attaches the currently assigned command executor to this instance. The command will
093         * be enabled by default unless the executor is a {@link GuardedActionCommandExecutor}, 
094         * in which case the command will be assigned the enabled state of the executor. 
095         */
096        private void attachCommandExecutor() {
097            if (this.commandExecutor instanceof GuardedActionCommandExecutor) {
098                GuardedActionCommandExecutor dynamicHandler = (GuardedActionCommandExecutor)commandExecutor;
099                setEnabled(dynamicHandler.isEnabled());
100                subscribeToGuardedCommandDelegate();
101            }
102            else {
103                setEnabled(true);
104            }
105            if (logger.isDebugEnabled()) {
106                logger.debug("Command executor '" + this.commandExecutor + "' attached.");
107            }
108        }
109    
110        private void subscribeToGuardedCommandDelegate() {
111            this.guardRelay = new PropertyChangeListener() {
112                public void propertyChange(PropertyChangeEvent evt) {
113                    setEnabled(((GuardedActionCommandExecutor)commandExecutor).isEnabled());
114                }
115            };
116            ((GuardedActionCommandExecutor)commandExecutor).addEnabledListener(guardRelay);
117        }
118    
119        /**
120         * Detaches the current executor from this command and sets the command to disabled state. 
121         */
122        public void detachCommandExecutor() {
123            if (this.commandExecutor instanceof GuardedActionCommandExecutor) {
124                unsubscribeFromGuardedCommandDelegate();
125            }
126            this.commandExecutor = null;
127            setEnabled(false);
128            logger.debug("Command delegate detached.");
129        }
130    
131        private void unsubscribeFromGuardedCommandDelegate() {
132            ((GuardedActionCommandExecutor)this.commandExecutor).removeEnabledListener(guardRelay);
133        }
134    
135        /**
136         * Executes this command by delegating to the currently assigned executor.
137         */
138        protected void doExecuteCommand() {
139            if (this.commandExecutor instanceof ParameterizableActionCommandExecutor) {
140                ((ParameterizableActionCommandExecutor) this.commandExecutor).execute(getParameters());
141            }
142            else {
143                    if (this.commandExecutor != null) {
144                            this.commandExecutor.execute();
145                    }
146            }
147        }
148    
149    }