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 }