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.support;
017    
018    import java.util.Hashtable;
019    import java.util.Iterator;
020    import java.util.LinkedList;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.springframework.richclient.command.AbstractCommand;
027    import org.springframework.richclient.command.ActionCommand;
028    import org.springframework.richclient.command.ActionCommandExecutor;
029    import org.springframework.richclient.command.CommandGroup;
030    import org.springframework.richclient.command.CommandNotOfRequiredTypeException;
031    import org.springframework.richclient.command.CommandRegistry;
032    import org.springframework.richclient.command.CommandRegistryEvent;
033    import org.springframework.richclient.command.CommandRegistryListener;
034    import org.springframework.richclient.command.TargetableActionCommand;
035    import org.springframework.richclient.util.Assert;
036    import org.springframework.util.ClassUtils;
037    import org.springframework.util.ObjectUtils;
038    
039    /**
040     * The default implementation of the {@link CommandRegistry} interface. This implementation 
041     * may act as the child of another registry, allowing for a hierarchy of registries to be created.
042     * If a command is requested from this registry but cannot be found, the request will be delegated
043     * to the parent registry.
044     * 
045     * 
046     * @author Keith Donald
047     * @author Kevin Stembridge
048     */
049    public class DefaultCommandRegistry implements CommandRegistry, CommandRegistryListener {
050        
051        /**
052         * Class logger, available to subclasses.
053         */
054        protected final Log logger = LogFactory.getLog(getClass());
055    
056        private final List commandRegistryListeners = new LinkedList();
057    
058        private final Map commandMap = new Hashtable();
059    
060        private CommandRegistry parent;
061    
062        /**
063         * Creates a new uninitialized {@code DefaultCommandRegistry}.
064         */
065        public DefaultCommandRegistry() {
066            //do nothing
067        }
068    
069        /**
070         * Creates a new {@code DefaultCommandRegistry} as a child of the given registry. The newly
071         * created instance will be added as a listener on the parent registry.
072         *
073         * @param parent The parent registry. May be null.
074         */
075        public DefaultCommandRegistry(CommandRegistry parent) {
076            internalSetParent(parent);
077        }
078    
079        /**
080         * Sets the given registry to be the parent of this instance. If the new parent is not null,
081         * this instance will act as a registry listener on it.
082         *
083         * @param parent The parent registry. May be null.
084         */
085        public void setParent(CommandRegistry parent) {
086            internalSetParent(parent);
087        }
088        
089        /**
090         * This method is provided as a private helper so that it can be called by the constructor, 
091         * instead of the constructor having to call the public overridable setParent method. 
092         */
093        private void internalSetParent(CommandRegistry parentRegistry) {
094            
095            if (!ObjectUtils.nullSafeEquals(this.parent, parentRegistry)) {
096                
097                if (this.parent != null) {
098                    this.parent.removeCommandRegistryListener(this);
099                }
100                
101                this.parent = parentRegistry;
102                
103                if (this.parent != null) {
104                    this.parent.addCommandRegistryListener(this);
105                }
106                
107            }
108            
109        }
110    
111        /**
112         * {@inheritDoc}
113         */
114        public void commandRegistered(CommandRegistryEvent event) {
115            Assert.required(event, "event");
116            fireCommandRegistered(event.getCommand());
117        }
118    
119        /**
120         * {@inheritDoc}
121         * @deprecated
122         */
123        public ActionCommand getActionCommand(String commandId) {
124            
125            if (logger.isDebugEnabled()) {
126                logger.debug("Attempting to retrieve ActionCommand with id ["
127                             + commandId
128                             + "] from the command registry.");
129            }
130            
131            Object command = getCommand(commandId, ActionCommand.class);
132            
133            return (ActionCommand) command;
134            
135        }
136    
137        /**
138         * {@inheritDoc}
139         * @deprecated
140         */
141        public CommandGroup getCommandGroup(String groupId) {
142            
143            if (logger.isDebugEnabled()) {
144                logger.debug("Attempting to retrieve command group with id ["
145                             + groupId
146                             + "] from the command registry.");
147            }
148            
149            Object command = getCommand(groupId, CommandGroup.class);
150            
151            return (CommandGroup) command;
152            
153        }
154    
155        /**
156         * {@inheritDoc}
157         * @deprecated
158         */
159        public boolean containsActionCommand(String commandId) {
160            
161            if (commandMap.containsKey(commandId)) {
162                return true;
163            }
164            
165            if (parent != null) {
166                return parent.containsActionCommand(commandId);
167            }
168            
169            return false;
170            
171        }
172    
173        /**
174         * {@inheritDoc}
175         * @deprecated
176         */
177        public boolean containsCommandGroup(String groupId) {
178            if (commandMap.get(groupId) instanceof CommandGroup) {
179                return true;
180            }
181            if (parent != null) {
182                return parent.containsCommandGroup(groupId);
183            }
184            return false;
185        }
186    
187        /**
188         * {@inheritDoc}
189         */
190        public void registerCommand(AbstractCommand command) {
191            
192            Assert.notNull(command, "Command cannot be null.");
193            Assert.isTrue(command.getId() != null, "A command must have an identifier to be placed in a registry.");
194            
195            Object previousCommand = this.commandMap.put(command.getId(), command);
196            
197            if (previousCommand != null && logger.isWarnEnabled()) {
198                logger.warn("The command ["
199                            + previousCommand
200                            + "] was overwritten in the registry with the command ["
201                            + command
202                            + "]");
203            }
204            
205            if (command instanceof CommandGroup) {
206                ((CommandGroup) command).setCommandRegistry(this);
207            }
208            
209            if (logger.isDebugEnabled()) {
210                logger.debug("Command registered '" + command.getId() + "'");
211            }
212            
213            fireCommandRegistered(command);
214            
215        }
216    
217        /**
218         * Fires a 'commandRegistered' {@link CommandRegistryEvent} for the given command to all 
219         * registered listeners. 
220         *
221         * @param command The command that has been registered. Must not be null.
222         * 
223         * @throws IllegalArgumentException if {@code command} is null.
224         */
225        protected void fireCommandRegistered(AbstractCommand command) {
226            
227            Assert.required(command, "command");
228            
229            if (commandRegistryListeners.isEmpty()) {
230                return;
231            }
232            
233            CommandRegistryEvent event = new CommandRegistryEvent(this, command);
234            
235            for (Iterator i = commandRegistryListeners.iterator(); i.hasNext();) {
236                ((CommandRegistryListener)i.next()).commandRegistered(event);
237            }
238            
239        }
240    
241        /**
242         * {@inheritDoc}
243         */
244        public void setTargetableActionCommandExecutor(String commandId, ActionCommandExecutor executor) {
245            
246            Assert.required(commandId, "commandId");
247            
248            TargetableActionCommand command 
249                    = (TargetableActionCommand) getCommand(commandId, TargetableActionCommand.class);
250                
251            if (command != null) {
252                command.setCommandExecutor(executor);
253            }
254            
255        }
256    
257        /**
258         * {@inheritDoc}
259         */
260        public void addCommandRegistryListener(CommandRegistryListener listener) {
261    
262            if (logger.isDebugEnabled()) {
263                logger.debug("Adding command registry listener " + listener);
264            }
265            
266            commandRegistryListeners.add(listener);
267            
268        }
269    
270        /**
271         * {@inheritDoc}
272         */
273        public void removeCommandRegistryListener(CommandRegistryListener listener) {
274            
275            if (logger.isDebugEnabled()) {
276                logger.debug("Removing command registry listener " + listener);
277            }
278            
279            commandRegistryListeners.remove(listener);
280            
281        }
282    
283        /**
284         * Returns the parent registry of this instance.
285         *
286         * @return The parent registry, or null.
287         */
288        public CommandRegistry getParent() {
289            return parent;
290        }
291    
292        /**
293         * {@inheritDoc}
294         */
295        public boolean containsCommand(String commandId) {
296            
297            Assert.required(commandId, "commandId");
298            
299            if (this.commandMap.containsKey(commandId)) {
300                return true;
301            }
302            
303            if (this.parent != null) {
304                return this.parent.containsCommand(commandId);
305            }
306            
307            return false;
308            
309        }
310    
311        /**
312         * {@inheritDoc}
313         */
314        public Object getCommand(String commandId) {
315            return getCommand(commandId, null);
316        }
317    
318        /**
319         * {@inheritDoc}
320         */
321        public Object getCommand(String commandId, Class requiredType) throws CommandNotOfRequiredTypeException {
322            
323            Assert.required(commandId, "commandId");
324            
325            Object command = this.commandMap.get(commandId);
326            
327            if (command == null && this.parent != null) {
328                command = this.parent.getCommand(commandId);
329            }
330            
331            if (command == null) {
332                return null;
333            }
334            
335            if (requiredType != null && !ClassUtils.isAssignableValue(requiredType, command)) {
336                throw new CommandNotOfRequiredTypeException(commandId, requiredType, command.getClass());
337            }
338            
339            return command;
340            
341        }
342    
343        /**
344         * {@inheritDoc}
345         */
346        public Class getType(String commandId) {
347            
348            Assert.required(commandId, "commandId");
349            
350            Object command = getCommand(commandId);
351            
352            if (command == null) {
353                return null;
354            }
355            else {
356                return command.getClass();
357            }
358            
359        }
360    
361        /**
362         * {@inheritDoc}
363         */
364        public boolean isTypeMatch(String commandId, Class targetType) {
365            
366            Assert.required(commandId, "commandId");
367            Assert.required(targetType, "targetType");
368            
369            Class commandType = getType(commandId);
370            
371            if (commandType == null) {
372                return false;
373            }
374            else {
375                return ClassUtils.isAssignable(targetType, commandType);
376            }
377            
378        }
379    
380    }