package apes.controllers; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.EventObject; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * <p> * This class handles all controller actions. All controllers should extend * ApplicationController, that in turn extend this class. This means that all * controllers will handle events the same way. * </p> * <h4>Usage</h4> * <p> * If you want to connect an event to a <code>Component</code>, Say for example * a <code>JButton</code>, you add a listener as usual: * </p> * * <pre> * JButton button = new JButton("Some button"); * button.addActionListener(controller); * </pre> * <p> * Note that the event should <strong>always</strong> go to the controller. * </p> * <p> * After this there's one more thing to do. And that is to give the Component a * name with the {@link java.awt.Component#setName(String name) setName} method. * </p> * * <pre> * button.setName("action"); * </pre> * <p> * The name of the method that will be called in the controller, that was * connected to the component, is the one with the same name as the component * name. This however, is only true, if there's a method in the controller with * that name, taking no arguments. So for the above example the * <code>action</code> method in the <code>controller</code> controller will be * called when <code>button</code> is clicked. * </p> * <p> * However. If there is no method named action in the controller. A method * defined in {@link ApplicationController#methodMissing ApplicationController} * called <code>methodMissing</code> is called. This is handy for dynamic action * calling. So in your controller, you can override this method to get some * other behavior. * </p> * <p> * Before a method is called, {@link ApplicationController#beforeFilter * beforeFilter} is called. And after the method is called, * {@link ApplicationController#afterFilter afterFilter} is called. However. The * action is only called if there's no exception thrown in the before filter. * This can be used to not call an action on some condition in the before * filter. * </p> * <p> * Two variables are by default available in the controllers. * <ul> * <li>event - Is the event that was fired.</li> * <li>name - Is the name of the component that the event was fired on.</li> * </ul> * </p> * <h4>Troubleshooting</h4> * <p> * If the above does not work. Start by reading the comments in * {@link ApplicationController ApplicationController}. * </p> * <p> * If you done that and it still doesn't work, make sure that this class * implements the listener class you want to use. Say for example that you want * to add a <code>ComponentListener</code> to your controller. Then this class * must implement <code>ComponentListener</code> and have all methods from that * interface. In this case: * </p> * <ul> * <li>void componentHidden(ComponentEvent e)</li> * <li>void componentMoved(ComponentEvent e)</li> * <li>void componentResized(ComponentEvent e)</li> * <li>void componentShown(ComponentEvent e)</li> * </ul> * <p> * And in each of these methods you should first set the event instance variable * to the event that is passed to the method. And then call * {@link ActionController#callActionByName callActionByName}. * </p> * <p> * One more thing to check is if your controller is created or not. Because if * you send null to <code>addActionListener</code>, you don't get any errors. * </p> * * @author Johan Andersson (johandy@student.chalmers.se) */ public abstract class ActionController implements ActionListener, ChangeListener { /** * The event that was fired. Controllers can via this variable get the * Component who fired the event. */ protected EventObject event; /** * The name of the component. */ protected String name; public void actionPerformed(ActionEvent e) { this.event = e; callActionByName(); } public void stateChanged(ChangeEvent e) { this.event = e; callActionByName(); } /** * Will call the method in the controller with the same name as the components * name. */ private void callActionByName() { // The action that is to be called is the method with the same // name as the name for the component that triggered the event. this.name = ((Component)event.getSource()).getName(); try { try { callAction("beforeFilter"); callAction(name); } catch(InvocationTargetException e) {} callAction("afterFilter"); } catch(NoSuchMethodException e) { try { callAction("methodMissing"); } catch(Exception ex) { ex.printStackTrace(); } } catch(Exception e) { e.printStackTrace(); } } /** * Call that method on this (the controller the view is connected to) class. * * @param name a <code>String</code> value * @exception NoSuchMethodException If method does not exits. * @exception Exception If any other error occurs. */ private void callAction(String name) throws NoSuchMethodException, InvocationTargetException, Exception { Method method = this.getClass().getMethod(name); method.invoke(this); } }