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 }