View Javadoc

1   /*
2    *  Copyright 2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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 }