View Javadoc

1   /*
2    * Copyright (c) 1998, 2005 Gargoyle Software Inc. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    *
7    * 1. Redistributions of source code must retain the above copyright notice,
8    *    this list of conditions and the following disclaimer.
9    * 2. Redistributions in binary form must reproduce the above copyright notice,
10   *    this list of conditions and the following disclaimer in the documentation
11   *    and/or other materials provided with the distribution.
12   * 3. The end-user documentation included with the redistribution, if any, must
13   *    include the following acknowledgment:
14   *
15   *       "This product includes software developed by Gargoyle Software Inc.
16   *        (http://www.GargoyleSoftware.com/)."
17   *
18   *    Alternately, this acknowledgment may appear in the software itself, if
19   *    and wherever such third-party acknowledgments normally appear.
20   * 4. The name "Gargoyle Software" must not be used to endorse or promote
21   *    products derived from this software without prior written permission.
22   *    For written permission, please contact info@GargoyleSoftware.com.
23   * 5. Products derived from this software may not be called "GSBase", nor may
24   *    "GSBase" appear in their name, without prior written permission of
25   *    Gargoyle Software Inc.
26   *
27   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
28   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
30   * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
33   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   */
38  package com.gargoylesoftware.base.gui;
39  
40  import com.gargoylesoftware.base.util.DetailedIllegalArgumentException;
41  import com.gargoylesoftware.base.util.DetailedNullPointerException;
42  import javax.swing.AbstractAction;
43  import java.awt.event.ActionEvent;
44  import java.lang.reflect.Method;
45  import java.lang.reflect.InvocationTargetException;
46  
47  /***
48   * A swing "action" that uses reflection to find and invoke a given target
49   * method.<p>
50   *
51   * ReflectedAction is a Swing "action" that uses reflection to call a specific target
52   * method when the action is fired.  Actions are often implemented using inner classes which
53   * can be very inefficient in terms of performance and deployment size.  This action is a tiny
54   * bit slower when it is actually fired but is smaller and generally faster otherwise due to
55   * the fact that those inner classes are no longer needed.
56   *
57   * <pre>
58   * Action searchAction = new ReflectedAction(this, "performSearch");
59   * </pre>
60   *
61   * @version $Revision: 1.5 $
62   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
63   */
64  public class ReflectedAction extends AbstractAction {
65  
66      private static final long serialVersionUID = -3881811288492789994L;
67  	private final Object object_;
68      private final String methodName_;
69  
70  
71      /***
72       * Create a reflected action with an object and the name of the method that
73       * will be invoked on that object when the action is fired.
74       *
75       * @param object The object upon which we will invoke a method when
76       * the action is fired
77       * @param methodName The name of the method that we will invoke.  This method
78       * must take either no parameters or one parameter which is an ActionEvent.
79       * @throws IllegalArgumentException if there are no matching methods.
80       */
81      public ReflectedAction( final Object object, final String methodName )
82          throws IllegalArgumentException {
83  
84          assertNotNull("object", object);
85          assertNotNull("methodName", methodName);
86  
87          findMethodOnObject(object, methodName);
88  
89          object_ = object;
90          methodName_ = methodName;
91      }
92  
93  
94      /***
95       * The action has been fired so now we use reflection to invoke the method that
96       * was specified previously.
97       *
98       * @param event The ActionEvent
99       */
100     public void actionPerformed( final ActionEvent event ) {
101         final Method method = findMethodOnObject(object_, methodName_);
102         final Object parameters[];
103         if( method.getParameterTypes().length == 0 ) {
104             parameters = new Object[0];
105         }
106         else {
107             parameters = new Object[]{event};
108         }
109 
110         try {
111             method.invoke(object_, parameters);
112         }
113         catch( final IllegalAccessException e ) {
114             exceptionThrown(e);
115         }
116         catch( final InvocationTargetException e ) {
117             exceptionThrown(e);
118         }
119         catch( final IllegalArgumentException e ) {
120             exceptionThrown(e);
121         }
122     }
123 
124 
125     /***
126      * Return the Method object that corresponds to the specified method name.
127      * The method may take either no arguments or may take an ActionEvent.
128      *
129      * @param object The object that contains the method
130      * @param methodName The name of the method that we're looking for
131      * @return The method that matches the specified name.
132      * @throws IllegalArgumentException If there aren't any methods that match.
133      * @throws SecurityException If we do not have access to this method.
134      */
135     private Method findMethodOnObject( final Object object, final String methodName ) {
136         final Class clazz = object.getClass();
137         Method method = null;
138 
139         try {
140             method = clazz.getDeclaredMethod(methodName, new Class[0]);
141         }
142         catch( final NoSuchMethodException e ) {
143             try {
144                 method = clazz.getDeclaredMethod(methodName, new Class[]{ActionEvent.class});
145             }
146             catch( final NoSuchMethodException ex ) {
147                 throw new DetailedIllegalArgumentException("methodName", methodName, "No such method");
148             }
149         }
150 
151         // If we wouldn't normally have access to this method then
152         // request access now.  If we are denied then a SecurityException
153         // will be thrown.
154         if( method.isAccessible() == false ) {
155             method.setAccessible(true);
156         }
157         return method;
158     }
159 
160 
161     /***
162      * Callback that will be invoked if an exception is thrown during the
163      * reflected method call.<p>
164      *
165      * Override this to provide custom error handling. Default behaviour
166      * is to print a stack trace.
167      *
168      * @param exception The exception
169      */
170     protected void exceptionThrown( final Exception exception ) {
171         if( exception instanceof InvocationTargetException ) {
172             ((InvocationTargetException)exception).getTargetException().printStackTrace();
173         }
174         else {
175             exception.printStackTrace();
176         }
177     }
178 
179 
180     /***
181      * Throw an exception if the specified object is null
182      * @param fieldName The name of the paremeter we are checking
183      * @param object The value of the parameter we are checking
184      */
185     protected final void assertNotNull( final String fieldName, final Object object ) {
186         if( object == null ) {
187             throw new DetailedNullPointerException(fieldName);
188         }
189     }
190 }