1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
152
153
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 }