1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.functors.transformer;
17
18 import net.sf.collections15.Transformer;
19 import net.sf.collections15.internal.ReflectionUtils;
20 import net.sf.collections15.functors.FunctorException;
21
22 import java.io.Serializable;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25
26
27 /***
28 * <code>Transformer</code> implementation whose {@link #transform} method
29 * invokes a specified method on the input object by reflection, returning the
30 * resulting return value as the output.
31 *
32 * @author Stephen Colebourne
33 * @author Chris Lambrou (port to Java 5.0)
34 * @since Collections15 1.0
35 */
36 public class InvokerTransformer <I, O> implements Transformer<I, O>, Serializable
37 {
38
39 static final long serialVersionUID = 1321307589091795130L;
40
41 /***
42 * The name of the method to call on an input object.
43 */
44 private final String methodName;
45
46 /***
47 * The the parameters types of the method invoked on the input object.
48 */
49 private final Class[] parameterTypes;
50
51 /***
52 * The arguments to pass to the method invoked on the input object.
53 */
54 private final Object[] arguments;
55
56 /***
57 * Returns an instance that transforms an input object by invoking a method
58 * of the specified name and no arguments via reflection, and returning the
59 * return value as the output object.
60 *
61 * @param methodName The name of the method to invoke on an input object.
62 *
63 * @return An instance that transforms an input object by invoking a method
64 * of the specified name and no arguments via reflection, and
65 * returning the return value as the output object.
66 *
67 * @throws IllegalArgumentException Thrown if the method name argument is
68 * <code>null</code>.
69 */
70 public static <I, O> InvokerTransformer<I, O> getInstance(String methodName)
71 {
72 return getInstance(methodName, null, null);
73 }
74
75 /***
76 * Returns an instance that transforms an input object by invoking a method
77 * of the specified signature with the specified arguments via reflection,
78 * and returning the return value as the output object.
79 *
80 * @param methodName The name of the method to invoke on an input
81 * object.
82 * @param parameterTypes The parameter types of the method to invoke on an
83 * input object. May be <code>null</code> or empty if
84 * <code>arguments</code> is also <code>null</code> or
85 * empty, in which case the parameterless mehod of the
86 * specified name will be used. The contents of this
87 * array are defensively copied.
88 * @param arguments The arguments to pass to the method invoked on an
89 * input <code>Class</code>.
90 *
91 * @return An instance that transforms an input object by invoking a method
92 * of the specified signature with the specified arguments via
93 * reflection, and returning the return value as the output object.
94 *
95 * @throws IllegalArgumentException Thrown if the method name argument is
96 * <code>null</code>.
97 * @throws IllegalArgumentException Thrown if either of <code>parameterTypes</code>
98 * or <code>arguments</code> is
99 * <code>null</code>, but the other is
100 * not.
101 * @throws IllegalArgumentException Thrown if any element in <code>parameterTypes</code>
102 * is <code>null</code>.
103 * @throws IllegalArgumentException Thrown if any none-<code>null</code>
104 * element in <code>arguments</code> is not
105 * an instance of its corresponding
106 * <code>parameterTypes</code> class.
107 */
108 public static <I, O> InvokerTransformer<I, O> getInstance(String methodName, Class[] parameterTypes, Object[] arguments)
109 {
110 return new InvokerTransformer<I, O>(methodName, parameterTypes, arguments);
111 }
112
113 /***
114 * Create a new instance that transforms an input object by invoking a
115 * method of the specified signature with the specified arguments via
116 * reflection, and returning the return value as the output object.
117 *
118 * @param methodName The name of the method to invoke on an input
119 * object.
120 * @param parameterTypes The parameter types of the method to invoke on an
121 * input object. May be <code>null</code> or empty if
122 * <code>arguments</code> is also <code>null</code> or
123 * empty, in which case the parameterless mehod of the
124 * specified name will be used. The contents of this
125 * array are defensively copied.
126 * @param arguments The arguments to pass to the method invoked on an
127 * input <code>Class</code>.
128 *
129 * @throws IllegalArgumentException Thrown if the method name argument is
130 * <code>null</code>.
131 * @throws IllegalArgumentException Thrown if either of <code>parameterTypes</code>
132 * or <code>arguments</code> is
133 * <code>null</code>, but the other is
134 * not.
135 * @throws IllegalArgumentException Thrown if any element in <code>parameterTypes</code>
136 * is <code>null</code>.
137 * @throws IllegalArgumentException Thrown if any none-<code>null</code>
138 * element in <code>arguments</code> is not
139 * an instance of its corresponding
140 * <code>parameterTypes</code> class.
141 */
142 protected InvokerTransformer(String methodName, Class[] parameterTypes, Object[] arguments)
143 {
144 if (methodName == null) {
145 throw new IllegalArgumentException("The name of the method to invoke must not be null");
146 }
147 this.methodName = methodName;
148
149 int numParams = parameterTypes == null ? 0 : parameterTypes.length;
150 int numArgs = arguments == null ? 0 : arguments.length;
151 if (numParams != numArgs) {
152 throw new IllegalArgumentException("Number of parameter types, " + numParams
153 + ", does not match the number of arguments, " + numArgs);
154 }
155 this.parameterTypes = new Class[numParams];
156 this.arguments = new Object[numArgs];
157
158 for (int i = 0; i < numParams; i++) {
159 Class parameterType = parameterTypes[i];
160 if (parameterType == null) {
161 throw new IllegalArgumentException("null parameter type at index " + i);
162 }
163 this.parameterTypes[i] = parameterType;
164 Object argument = arguments[i];
165 if (ReflectionUtils.isAssignableTo(parameterType, argument) == false) {
166 throw new IllegalArgumentException("object at index " + i
167 + " does not match its corresponding parameter type: expected "
168 + parameterType.getName() + ", actual " + argument.getClass().getName());
169 }
170 this.arguments[i] = argument;
171 }
172 }
173
174 /***
175 * Transforms the input by invoking a method on it and returning the
176 * resulting return value.
177 *
178 * @param input The input object to transform.
179 *
180 * @return The return value of the method invoked on the input object. If
181 * the return type of the method is <code>void</code>,
182 * <code>null</code> is returned.
183 *
184 * @throws FunctorException Thrown if any of the following is true. <ul>
185 * <li>The input object is <code>null</code>.</li>
186 * <li>A method with the appropriate name and
187 * signature doesn't exist or isn't public.</li>
188 * <li>The method could not be instantiated for any
189 * other reason.</li> <li>The method itself threw
190 * an exception.</li> </ul>
191 */
192 public O transform(I input) throws FunctorException
193 {
194 try {
195 Class cls = input.getClass();
196 Method method = cls.getMethod(methodName, parameterTypes);
197 return (O) method.invoke(input, arguments);
198 }
199 catch (NoSuchMethodException ex) {
200 throw new FunctorException("InvokerTransformer: The method '" + methodName + "' on '" + input.getClass() + "' does not exist");
201 }
202 catch (IllegalAccessException ex) {
203 throw new FunctorException("InvokerTransformer: The method '" + methodName + "' on '" + input.getClass() + "' cannot be accessed");
204 }
205 catch (InvocationTargetException ex) {
206 throw new FunctorException("InvokerTransformer: The method '" + methodName + "' on '" + input.getClass() + "' threw an exception", ex);
207 }
208 catch (ClassCastException ex) {
209 throw new FunctorException("InvokerTransformer: The method '" + methodName + "' on '" + input.getClass() + "' returned an unexpected type", ex);
210 }
211 catch (NullPointerException ex) {
212 throw new FunctorException("InvokerTransformer: The input is null", ex);
213 }
214 }
215
216 }