package org.freehep.util.commanddispatcher; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Observable; /** * A default implementation of CommandGroup. * * Typically applications provide their own CommandProcessor(s) which extend this base class * and which can handle a set of commands. By default CommandProcessor's acceptCommand method * uses reflection to search for methods in the subClass which correspond to specific commands, * although subclasses could also override the acceptCommand method to implement a different * scheme. The default scheme looks for methods of type: * <pre> * public void onXXX() * </pre> * to handle the command XXX. Also * <pre> * public void enableXXX(CommandState state) * </pre> * to determine if the command is active or not. * * @author Tony Johnson (tonyj@slac.stanford.edu) * @version $Id: CommandProcessor.java 8584 2006-08-10 23:06:37Z duns $ */ public class CommandProcessor extends Observable implements CommandGroup { private static final Class[] noArg = { }; private static final Object[] noArgs = { }; private static final Class[] boolArg = { java.lang.Boolean.TYPE }; private static final Class[] simpleEnableArg = { CommandState.class }; private static final Class[] booleanEnableArg = { BooleanCommandState.class }; private CommandTargetManager manager; public void setChanged() { if (!hasChanged()) { super.setChanged(); javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { notifyObservers(); clearChanged(); } }); } } public void setManager(CommandTargetManager m) { this.manager = m; } /** * The CommandTargetManager calls acceptCommand to find out if this CommandProcessor * can respond to the specified command. If it can it returns a CommandTarget for the command, * otherwise it returns null. * @param command The command to test for */ public CommandTarget acceptCommand(String command) { return acceptCommand(getClass(), command); } /** * Uses reflection to check if the specified class has an "on" or "enable" method * for this comamnd. * @param klass The class to check * @param command The command to test for */ protected CommandTarget acceptCommand(Class klass, String command) { String t = translate(command); String c = "on" + t; String e = "enable" + t; Method mc; Method me; try { mc = klass.getMethod(c, noArg); try { me = klass.getMethod(e, simpleEnableArg); } catch (NoSuchMethodException x) { me = null; } try // Support for RadioMenuItem { if (me == null) me = klass.getMethod(e, booleanEnableArg); } catch (NoSuchMethodException x) { me = null; } return new SimpleTarget(mc, me); } catch (NoSuchMethodException x) {} try { mc = klass.getMethod(c, boolArg); try { me = klass.getMethod(e, booleanEnableArg); } catch (NoSuchMethodException x) { me = null; } return new BooleanTarget(mc, me); } catch (NoSuchMethodException x) {} return null; } protected void invoke(Method method, Object[] args) throws IllegalAccessException, InvocationTargetException { method.invoke(this, args); } /** * Override this method to provide application specific handling * for errors generated during command dispatch */ protected void invokeCommand(SimpleTarget t) { try { t.doCommand(); } catch (CommandInvocationException x) { manager.handleCommandError(x.getTargetException()); } } /** * Override this method to provide application specific handling * for errors generated during command dispatch */ protected void invokeCommand(BooleanTarget t, boolean arg) { try { t.doCommand(arg); } catch (CommandInvocationException x) { manager.handleCommandError(x.getTargetException()); } } protected void invokeCommand(Method method, Object[] args) throws IllegalAccessException, InvocationTargetException { invoke(method, args); } protected void invokeEnable(Method method, Object[] args) throws IllegalAccessException, InvocationTargetException { invoke(method, args); } /** * Translates a command. The default implementation removes trailing ..., and * any spaces and then uppercases the first character . */ protected String translate(String command) { if (command.endsWith("...")) command = command.substring(0, command.length() - 3); for (int i = command.indexOf(" "); i >= 0; i = command.indexOf(" ")) command = command.substring(0, i) + command.substring(i + 1); if ((command.length() < 2) || Character.isUpperCase(command.charAt(0))) return command; else return command.substring(0, 1).toUpperCase() + command.substring(1); } /** * Boolean target is an implementation of CommandTarget * for command targets which can be swithced on/off. (Typically * corresponding to JCheckBoxMenuItem or JRadioButtonMenuItem). */ protected class BooleanTarget implements BooleanCommandTarget { private Method m_command; private Method m_enable; BooleanTarget(Method command, Method enable) { m_command = command; m_enable = enable; try { command.setAccessible(true); if (enable != null) enable.setAccessible(true); } catch (SecurityException x) {} } public CommandGroup getGroup() { return CommandProcessor.this; } public void doCommand(boolean arg) throws CommandInvocationException { try { Object[] args = { new Boolean(arg) }; invokeCommand(m_command, args); } catch (IllegalAccessException x) { throw new RuntimeException("IllegalAccessException during command invocation"); } catch (InvocationTargetException x) { throw new CommandInvocationException(x.getTargetException()); } } public void enable(CommandState state) { if (m_enable == null) state.setEnabled(true); else { try { Object[] args = { (BooleanCommandState) state }; invokeEnable(m_enable, args); } catch (IllegalAccessException x) { state.setEnabled(false); } catch (InvocationTargetException x) { state.setEnabled(false); } } } public void invoke(boolean arg) { CommandProcessor.this.invokeCommand(this, arg); } } /** * A SimpleTarget is an implementation of CommandTarget * */ protected class SimpleTarget implements SimpleCommandTarget { private Method m_command; private Method m_enable; SimpleTarget(Method command, Method enable) { m_command = command; m_enable = enable; try { command.setAccessible(true); if (enable != null) enable.setAccessible(true); } catch (SecurityException x) {} } public CommandGroup getGroup() { return CommandProcessor.this; } public void doCommand() throws CommandInvocationException { try { invokeCommand(m_command, noArgs); } catch (IllegalAccessException x) { throw new RuntimeException("IllegalAccessException during command invocation"); } catch (InvocationTargetException x) { throw new CommandInvocationException(x.getTargetException()); } } public void enable(CommandState state) { if (m_enable == null) state.setEnabled(true); else { try { Object[] args = { state }; invokeEnable(m_enable, args); } catch (IllegalAccessException x) { state.setEnabled(false); } catch (InvocationTargetException x) { state.setEnabled(false); } } } public void invoke() { CommandProcessor.this.invokeCommand(this); } } }