diff --git a/projects/samskivert/src/java/com/samskivert/swing/Controller.java b/projects/samskivert/src/java/com/samskivert/swing/Controller.java index 0a5a0588..c0cde96a 100644 --- a/projects/samskivert/src/java/com/samskivert/swing/Controller.java +++ b/projects/samskivert/src/java/com/samskivert/swing/Controller.java @@ -1,5 +1,5 @@ // -// $Id: Controller.java,v 1.10 2002/03/16 20:52:07 mdb Exp $ +// $Id: Controller.java,v 1.11 2002/03/21 00:53:38 mdb Exp $ // // samskivert library - useful routines for java programs // Copyright (C) 2001 Michael Bayne @@ -25,6 +25,8 @@ import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.lang.reflect.Method; + import javax.swing.JPanel; import javax.swing.event.AncestorEvent; @@ -144,12 +146,113 @@ public abstract class Controller * separate thread, for example via {@link * com.samskivert.swing.util.TaskMaster}. * + *

The default implementation of this method will reflect on the + * controller class, looking for a method that matches the name of the + * action event. A handler method must provide one of three + * signatures: one accepting no arguments, one including only a + * reference to the source object, or one including the source object + * and an extra argument (which can be used only if the action event + * is an instance of {@link CommandEvent}. For example: + * + *

+     * public void handleCancelClicked (Object source);
+     * public void handleTextEntered (Object source, String text);
+     * 
+ * + * The arguments to the method can be as specific or as generic as + * desired and reflection will perform the appropriate conversions at + * runtime. For example, a method could be declared like so: + * + *
+     * public void handleCancelClicked (JButton source);
+     * 
+ * + * One would have to ensure that the only action events generated with + * the action command string "Cancel" were generated by + * JButton instances if such a signature were used. + * * @param action The action to be processed. * * @return true if the action was processed, false if it should be * propagated up to the next controller in scope. */ - public abstract boolean handleAction (ActionEvent action); + public boolean handleAction (ActionEvent action) + { + Method method = null; + Object[] args = null; + + try { + // look for the appropriate method + String targetName = "handle" + action.getActionCommand(); + Method[] methods = getClass().getMethods(); + int mcount = methods.length; + + for (int i = 0; i < mcount; i++) { + if (methods[i].getName().equals(targetName)) { + // see if we can generate the appropriate arguments + args = generateArguments(methods[i], action); + // if we were able to, go ahead and use this method + if (args != null) { + method = methods[i]; + break; + } + } + } + + } catch (Exception e) { + Log.warning("Error searching for action handler method " + + "[controller=" + this + ", event=" + action + "]."); + return false; + } + + try { + if (method != null) { + method.invoke(this, args); + return true; + + } else { + return false; + } + + } catch (Exception e) { + Log.warning("Error invoking action handler [controller=" + this + + ", event=" + action + "]."); + // even though we choked, we still "handled" the action + return true; + } + } + + /** + * Used by {@link #handleAction} to generate arguments to the action + * handler method. + */ + protected Object[] generateArguments (Method method, ActionEvent action) + { + // figure out what sort of arguments are required by the method + Class[] atypes = method.getParameterTypes(); + + if (atypes == null || atypes.length == 0) { + return new Object[0]; + + } else if (atypes.length == 1) { + return new Object[] { action.getSource() }; + + } else if (atypes.length == 2) { + if (action instanceof CommandEvent) { + CommandEvent command = (CommandEvent)action; + return new Object[] { action.getSource(), + command.getArgument() }; + } + + Log.warning("Unable to map non-command event to " + + "handler method that requires extra " + + "argument [controller=" + this + + ", action=" + action + "]."); + } + + // we would have handled it, but we couldn't + return null; + } /** * Posts the specified action to the nearest controller in scope. The