1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 }