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.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 }