1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.collections15.functors.factory;
17
18 import net.sf.collections15.Factory;
19 import net.sf.collections15.functors.FunctorException;
20
21 import java.io.Serializable;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24
25 /***
26 * <code>Factory</code> implementation that creates a new object instance of a
27 * specified class using by reflection.
28 *
29 * @author Stephen Colebourne
30 * @author Chris Lambrou (port to Java 5.0)
31 * @since Collections15 1.0
32 */
33 public class InstantiateFactory <E> implements Factory<E>, Serializable
34 {
35
36 static final long serialVersionUID = 6067627626173705387L;
37
38 /***
39 * The class of the objects created by this <code>Factory</code>.
40 */
41 private final Class<? extends E> classToInstantiate;
42
43 /***
44 * The parameter types of the constructor used to instantiate the objects
45 * created by this <code>Factory</code>.
46 */
47 private final Class[] paramTypes;
48
49 /***
50 * The arguments passed to the constructor used to instantiate the objects
51 * created by this <code>Factory</code>.
52 */
53 private final Object[] args;
54
55 /***
56 * The constructor instance used to instantiate objects. Do not reference
57 * this directly - use {@link #getConstructor()} instead. This instance may
58 * be <code>null</code> due to serialisation. <code>getConstructor()</code>
59 * will lazily instantiate the constructor if necessary.
60 */
61 private transient Constructor<? extends E> constructor = null;
62
63 /***
64 * Returns the constructor, instantiating it if necessary.
65 *
66 * @return The constructor used to instatiate objects.
67 *
68 * @throws NoSuchMethodException Thrown if a constructor cannot be resolved
69 * using {@link #classToInstantiate} and
70 * {@link #paramTypes}.
71 */
72 private Constructor<? extends E> getConstructor() throws NoSuchMethodException
73 {
74 if (constructor == null) {
75 constructor = classToInstantiate.getConstructor(paramTypes);
76 }
77 return constructor;
78 }
79
80 /***
81 * Creates a <code>Factory</code> that creates instances of the specified
82 * class, using the parameterless constructor of the class.
83 *
84 * @param classToInstantiate The class that the <code>InstantiateFactory</code>
85 * should create instances of.
86 * @param paramTypes The types of the constructor parameters of the
87 * class to instantiate.
88 * @param args The arguments passed to the constructor used to
89 * instantiate the objects created by the
90 * <code>Factory</code>.
91 *
92 * @return A <code>Factory</code> that creates instances of the specified
93 * class, using the constructor with the specified parameter types
94 * and values.
95 *
96 * @throws IllegalArgumentException Thrown if the specified class is
97 * <code>null</code>, or deosn't have a
98 * public constructor with the specified
99 * parameter types.
100 * @throws IllegalArgumentException Thrown if either the <code>paramTypes</code>
101 * or <code>args</code> arrays are
102 * <code>null</code>, are of unequal
103 * length, or are otherwise invalid.
104 */
105 public static <T> Factory<T> getInstance(Class<? extends T> classToInstantiate, Class[] paramTypes, Object[] args)
106 {
107 paramTypes = (Class[]) paramTypes.clone();
108 args = (Object[]) args.clone();
109 return new InstantiateFactory<T>(classToInstantiate, paramTypes, args);
110 }
111
112 /***
113 * Creates a <code>Factory</code> that creates instances of the specified
114 * class, using the parameterless constructor of the class.
115 *
116 * @param classToInstantiate The class that the <code>InstantiateFactory</code>
117 * should create instances of.
118 *
119 * @return A <code>Factory</code> that creates instances of the specified
120 * class, using its parameterless constructor.
121 *
122 * @throws IllegalArgumentException Thrown if the specified class is
123 * <code>null</code>, or deosn't have a
124 * public parameterless constructor.
125 */
126 public static <T> InstantiateFactory<T> getInstance(Class<? extends T> classToInstantiate)
127 {
128 return new InstantiateFactory<T>(classToInstantiate, emptyClassArray, emptyObjectArray);
129 }
130
131 /***
132 * An empty array of classes - used to find the parameterless constructor of
133 * a class.
134 */
135 private static Class[] emptyClassArray = new Class[0];
136
137 /***
138 * An empty array of objects - used to find the parameterless constructor of
139 * a class.
140 */
141 private static Object[] emptyObjectArray = new Object[0];
142
143 /***
144 * Constructs a new <code>Factory</code> that creates instances of the
145 * specified class, using the parameterless constructor of the class.
146 *
147 * @param classToInstantiate The class that the <code>InstantiateFactory</code>
148 * should create instances of.
149 * @param paramTypes The types of the constructor parameters of the
150 * class to instantiate.
151 * @param args The arguments passed to the constructor used to
152 * instantiate the objects created by the
153 * <code>Factory</code>.
154 *
155 * @throws IllegalArgumentException Thrown if the specified class is
156 * <code>null</code>, or deosn't have a
157 * public constructor with the specified
158 * parameter types.
159 * @throws IllegalArgumentException Thrown if either the <code>paramTypes</code>
160 * or <code>args</code> arrays are
161 * <code>null</code>, are of unequal
162 * length, or are otherwise invalid.
163 */
164 protected InstantiateFactory(Class<? extends E> classToInstantiate, Class[] paramTypes, Object[] args)
165 {
166 if (classToInstantiate == null) {
167 throw new IllegalArgumentException("Class to instantiate must not be null");
168 }
169 if ((paramTypes == null) || (args == null) || (paramTypes.length != args.length)) {
170 throw new IllegalArgumentException("Parameter types must match the arguments");
171 }
172 int i = 0;
173 for (Class type : paramTypes) {
174 if (type == null) {
175 throw new IllegalArgumentException("null class found in paramTypes array");
176 }
177 Object arg = args[i];
178 if (type.isInstance(arg) == false) {
179 throw new IllegalArgumentException("object in args array does not match the corresponding parameter type");
180 }
181 }
182
183 this.classToInstantiate = classToInstantiate;
184 this.paramTypes = paramTypes;
185 this.args = args;
186 try {
187 getConstructor();
188 }
189 catch (NoSuchMethodException e) {
190 throw new IllegalArgumentException("Unable to resolve constructor: It must exist and be public", e);
191 }
192 }
193
194 /***
195 * Creates an object using the stored constructor, parameter types and
196 * arguments.
197 *
198 * @return A newly instantiated object.
199 */
200 public E create()
201 {
202 try {
203 Constructor<? extends E> constructor = getConstructor();
204 return constructor.newInstance(args);
205 }
206 catch (NoSuchMethodException ex) {
207 throw new FunctorException("InstantiateFactory: NoSuchMethodExseption", ex);
208 }
209 catch (InstantiationException ex) {
210 throw new FunctorException("InstantiateFactory: InstantiationException", ex);
211 }
212 catch (IllegalAccessException ex) {
213 throw new FunctorException("InstantiateFactory: Constructor must be public", ex);
214 }
215 catch (InvocationTargetException ex) {
216 throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
217 }
218 }
219
220 }