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  
19  import net.sf.collections15.Transformer;
20  import net.sf.collections15.internal.ReflectionUtils;
21  import net.sf.collections15.functors.FunctorException;
22  
23  import java.io.Serializable;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  import java.util.Map;
27  import java.util.HashMap;
28  
29  /***
30   * <code>Transformer</code> implementation that transforms a <code>Class</code>
31   * into an instance of that <code>Class</code> by reflection.
32   *
33   * @author Stephen Colebourne
34   * @author Chris Lambrou (port to Java 5.0)
35   * @since Collections15 1.0
36   */
37  public class InstantiateTransformer <E> implements Transformer<Class<? extends E>, E>, Serializable
38  {
39  
40      static final long serialVersionUID = -3029760751012766205L;
41  
42      /***
43       * The parameters types of the constructor invoked on an input
44       * <code>Class</code>.
45       */
46      private final Class[] parameterTypes;
47  
48      /***
49       * The arguments to pass to the constructor invoked on an input
50       * <code>Class</code>.
51       */
52      private final Object[] arguments;
53  
54      /***
55       * Returns an instance that transforms a <code>Class</code> object into a
56       * newly constructed instance of that class via reflection, using the
57       * parameterless constructor of the input class.
58       *
59       * @return An <code>InstantiateTransformer</code> that transforms a
60       *         <code>Class</code> object into a newly constructed instance of
61       *         that class via reflection, using the parameterless constructor of
62       *         the input class.
63       */
64      public static <E> InstantiateTransformer<E> getInstance()
65      {
66          return new InstantiateTransformer<E>(null, null);
67      }
68  
69      /***
70       * Returns an instance that transforms a <code>Class</code> object into a
71       * newly constructed instance of that class via reflection, using the
72       * constructor of the input class identified by the specified parameter
73       * types and values.
74       *
75       * @param parameterTypes The parameter types of the constructer to invoke on
76       *                       an input <code>Class</code>. May be
77       *                       <code>null</code> or empty if <code>arguments</code>
78       *                       is also <code>null</code> or empty, in which case
79       *                       the parameterless constructor will be used to
80       *                       create an output object from an input
81       *                       <code>Class</code>. The contents of this array are
82       *                       defensively copied.
83       * @param arguments      The arguments to pass to the constructor invoked on
84       *                       an input <code>Class</code>.
85       *
86       * @return An <code>InstantiateTransformer</code> that transforms a
87       *         <code>Class</code> object into a newly constructed instance of
88       *         that class via reflection, using the constructor of the input
89       *         class identified by the specified parameter types and values.
90       *
91       * @throws IllegalArgumentException Thrown if either of <code>parameterTypes</code>
92       *                                  or <code>arguments</code> is
93       *                                  <code>null</code>, but the other is
94       *                                  not.
95       * @throws IllegalArgumentException Thrown if any element in <code>parameterTypes</code>
96       *                                  is <code>null</code>.
97       * @throws IllegalArgumentException Thrown if any none-<code>null</code>
98       *                                  element in <code>arguments</code> is not
99       *                                  an instance of its corresponding
100      *                                  <code>parameterTypes</code> class.
101      */
102     public static <E> InstantiateTransformer<E> getInstance(Class[] parameterTypes, Object[] arguments)
103     {
104         return new InstantiateTransformer<E>(parameterTypes, arguments);
105     }
106 
107     /***
108      * Create a new instance that transforms a <code>Class</code> object into a
109      * newly constructed instance of that class via reflection.
110      *
111      * @param parameterTypes The parameter types of the constructer to invoke on
112      *                       an input <code>Class</code>. May be
113      *                       <code>null</code> or empty if <code>arguments</code>
114      *                       is also <code>null</code> or empty, in which case
115      *                       the parameterless constructor will be used to
116      *                       create an output object from an input
117      *                       <code>Class</code>. The contents of this array are
118      *                       defensively copied.
119      * @param arguments      The arguments to pass to the constructor invoked on
120      *                       an input <code>Class</code>.
121      *
122      * @throws IllegalArgumentException Thrown if either of <code>parameterTypes</code>
123      *                                  or <code>arguments</code> is
124      *                                  <code>null</code>, but the other is
125      *                                  not.
126      * @throws IllegalArgumentException Thrown if any element in <code>parameterTypes</code>
127      *                                  is <code>null</code>.
128      * @throws IllegalArgumentException Thrown if any none-<code>null</code>
129      *                                  element in <code>arguments</code> is not
130      *                                  an instance of its corresponding
131      *                                  <code>parameterTypes</code> class.
132      */
133     public InstantiateTransformer(Class[] parameterTypes, Object[] arguments)
134     {
135         int numParams = parameterTypes == null ? 0 : parameterTypes.length;
136         int numArgs = arguments == null ? 0 : arguments.length;
137         if (numParams != numArgs) {
138             throw new IllegalArgumentException("Number of parameter types, " + numParams
139                     + ", does not match the number of arguments, " + numArgs);
140         }
141         this.parameterTypes = new Class[numParams];
142         this.arguments = new Object[numArgs];
143 
144         for (int i = 0; i < numParams; i++) {
145             Class parameterType = parameterTypes[i];
146             if (parameterType == null) {
147                 throw new IllegalArgumentException("null parameter type at index " + i);
148             }
149             this.parameterTypes[i] = parameterType;
150             Object argument = arguments[i];
151             if (ReflectionUtils.isAssignableTo(parameterType, argument) == false) {
152                 throw new IllegalArgumentException("argument at index " + i
153                         + " does not match its corresponding parameter type: argument type "
154                         + parameterType.getName() + ", parameter type " + argument.getClass().getName());
155             }
156             this.arguments[i] = argument;
157         }
158     }
159 
160     /***
161      * Transforms the input <code>Class</code> object into a newly instantiated
162      * instance by reflection.
163      *
164      * @param input The input <code>Class</code> to transform into a new
165      *              instance of the class.
166      *
167      * @return A new instance of the <code>input</code> class.
168      *
169      * @throws FunctorException Thrown if any of the following is true. <ul>
170      *                          <li>The input object is <code>null</code>.</li>
171      *                          <li>A constructor with the appropriate signature
172      *                          doesn't exist or isn't public.</li> <li>The
173      *                          <code>Constructor</code> could not be
174      *                          instantiated for any other reason.</li> <li>The
175      *                          constructor itself threw an exception.</li>
176      *                          </ul>
177      */
178     public E transform(Class<? extends E> input)
179     {
180         try {
181             Constructor<? extends E> con = input.getConstructor(parameterTypes);
182             return con.newInstance(arguments);
183         }
184         catch (NoSuchMethodException ex) {
185             throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
186         }
187         catch (InstantiationException ex) {
188             throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
189         }
190         catch (IllegalAccessException ex) {
191             throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
192         }
193         catch (InvocationTargetException ex) {
194             throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
195         }
196         catch (NullPointerException ex) {
197             throw new FunctorException("InstantiateTransformer: Null input object", ex);
198         }
199     }
200 
201 }