View Javadoc

1   /*
2    ORG Usurper is a random value object generator library
3    Copyright (C) 2007  Pierre-Antoine Grégoire
4   
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9   
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14  
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  
20  package org.org.usurper;
21  
22  import java.beans.IntrospectionException;
23  import java.beans.Introspector;
24  import java.beans.PropertyDescriptor;
25  import java.lang.reflect.Constructor;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.Comparator;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  import java.util.TreeSet;
36  
37  import org.org.usurper.handlers.IHandler;
38  import org.org.usurper.handlers.basic.AbstractPropertyTypeHandler;
39  import org.org.usurper.handlers.basic.AbstractSpecificPropertyHandler;
40  import org.org.usurper.handlers.exceptions.NoHandlerDefinedException;
41  import org.org.usurper.model.HandledBeanProperty;
42  import org.org.usurper.model.HandledConstructorArg;
43  import org.org.usurper.model.ITargetDefinition;
44  import org.org.usurper.model.PropertyTypeDefinition;
45  import org.org.usurper.model.SpecificPropertyDefinition;
46  import org.org.usurper.setup.IUsurperGeneratorSetup;
47  import org.org.usurper.setup.ImmutableUsurperGeneratorSetup;
48  import org.org.usurper.setup.UsurperGeneratorSetup;
49  import org.org.usurper.setup.constants.OnMissingHandlers;
50  import org.org.usurper.setup.constants.PropertyWritingMechanism;
51  import org.org.usurper.utils.ReflectionUtils;
52  
53  /**
54   * This class defines a UsurperGenerator generator
55   * 
56   * @author pagregoire
57   */
58  public class UsurperGenerator<T> {
59  
60      private final Class<? extends T> usurpatedClass;
61      private Class<?>[] constructorParameterTypes = new Class<?>[0];
62      private ImmutableUsurperGeneratorSetup setup;
63  
64      /**
65       * This constructor creates an UsurperGenerator for the given Bean class.<br>
66       * It creates a default UsurperGeneratorSetup.
67       * 
68       * @param usurpatedClass
69       *            the generated class
70       * @see UsurperGeneratorSetup
71       */
72      public UsurperGenerator(Class<? extends T> usurpatedClass) {
73          this(usurpatedClass, new UsurperGeneratorSetup());
74      }
75  
76      /**
77       * This constructor is the same as the one-argument one, except that you can specify whether you want to ignore undefined handlers.<br>
78       * <code>
79       *  new UsurperGenerator&lt;T&gt;(Class&lt;T&gt; valueObjectClass, UsurperGenerator.SKIP_MISSING_HANDLERS)
80       * </code><br>
81       * or<br>
82       * <code>
83       * new UsurperGenerator&lt;T&gt;(Class&lt;T&gt; valueObjectClass, UsurperGenerator.FAIL_ON_MISSING_HANDLERS)
84       * </code>
85       * 
86       * @param valueObjectClass
87       * @param onMissingHandlers
88       * @deprecated prefer the use of the UsurperGeneratorSetup if you need to override the default behaviour
89       */
90      @SuppressWarnings("unchecked")
91      public UsurperGenerator(Class<? extends T> usurpatedClass, OnMissingHandlers onMissingHandlers) {
92          UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup();
93          mutableSetup.onMissingHandlers(onMissingHandlers);
94          this.setup = mutableSetup.getImmutable();
95          this.usurpatedClass = usurpatedClass;
96      }
97  
98      /**
99       * This constructor creates an UsurperGenerator for the given Bean class.<br>
100      * It takes a specific UsurperGeneratorSetup as a parameter.
101      * 
102      * @param usurpatedClass
103      *            the generated class
104      * @see UsurperGeneratorSetup
105      */
106     public UsurperGenerator(Class<? extends T> usurpatedClass, IUsurperGeneratorSetup usurperGeneratorSetup) {
107         this.replaceSetup(usurperGeneratorSetup);
108         this.usurpatedClass = usurpatedClass;
109     }
110 
111     /**
112      * This method generates an usurper object of the type specified as Generic for the UsurperGenerator instance.
113      * 
114      * @return an usurper object
115      */
116     public T generateUsurper() {
117         T valueObject = null;
118 
119         try {
120             valueObject = createInstance();
121 
122             if (valueObject == null) {
123                 throw new UsurperException("Can not find a usable constructor for bean:" + this.usurpatedClass.getName());
124             }
125 
126             PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(this.usurpatedClass).getPropertyDescriptors();
127             for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
128                 if (!propertyDescriptor.getName().equals("class")) {
129                     String propertyName = propertyDescriptor.getName();
130                     Class<?> propertyType = propertyDescriptor.getPropertyType();
131                     Method setter = propertyDescriptor.getWriteMethod();
132                     // if no setter method is available, then don't set anything
133                     // TODO provide a way to set privately accessible fields,
134                     // but this is not top priority.
135                     if (setter != null) {
136                         IHandler handler = getHandler(propertyName, propertyType);
137                         Object object = null;
138 
139                         if (handler == null) {
140                             if (this.setup.getOnMissingHandlers().equals(OnMissingHandlers.FAIL)) {
141                                 throw new NoHandlerDefinedException("No handler defined for property: " + propertyName + "(" + propertyType.getName() + ") in object of type: " + this.usurpatedClass.getName() + ".");
142                             }
143                         } else {
144                             HandledBeanProperty handledBeanProperty = new HandledBeanProperty(valueObject, propertyType, propertyName, this.setup);
145 
146                             try {
147                                 object = handler.handle(handledBeanProperty);
148                             } catch (NoHandlerDefinedException nhde) {
149                                 // if a bean property handler has thrown a
150                                 // missing handler error
151                                 // we should rethrow or swallow depending on the
152                                 // defined policy
153                                 if (this.setup.getOnMissingHandlers().equals(OnMissingHandlers.FAIL)) {
154                                     throw nhde;
155                                 }
156                             }
157                         }
158 
159                         if (object != null) {
160                             try {
161                                 switch (this.setup.getPropertyWritingMechanism()) {
162                                 case USE_SETTERS:
163                                     ReflectionUtils.setProperty(valueObject, setter, object);
164                                     break;
165                                 case MODIFY_ATTRIBUTES_DIRECTLY:
166                                     ReflectionUtils.setProperty(valueObject, propertyName, object);
167                                     break;
168                                 default:
169                                     break;
170                                 }
171 
172                             } catch (IllegalAccessException e) {
173                                 throw new UsurperException("Can not access bean property (illegal access):" + this.usurpatedClass.getName() + "." + propertyName, e);
174                             } catch (IllegalArgumentException e) {
175                                 throw new UsurperException("Wrong parameter for the setter of bean property:" + this.usurpatedClass.getName() + "." + propertyName, e);
176                             } catch (InvocationTargetException e) {
177                                 throw new UsurperException("Exception while setting value of bean property:" + this.usurpatedClass.getName() + "." + propertyName, e);
178                             } catch (SecurityException e) {
179                                 throw new UsurperException("Impossible to set the value of bean property:" + this.usurpatedClass.getName() + "." + propertyName, e);
180                             } catch (NoSuchFieldException e) {
181                                 throw new UsurperException("Specified bean property does not exist:" + this.usurpatedClass.getName() + "." + propertyName, e);
182                             }
183                         }
184                     }
185                 }
186             }
187 
188         } catch (IntrospectionException e) {
189             throw new UsurperException("Can not get Bean Info from bean:" + this.usurpatedClass.getName(), e);
190         }
191 
192         return valueObject;
193     }
194 
195     /**
196      * @param valueObject
197      * @return
198      * @throws SecurityException
199      * @throws UsurperException
200      */
201     private T createInstance() throws SecurityException, UsurperException {
202         T valueObject = null;
203         if (this.constructorParameterTypes.length != 0) {
204             try {
205                 Constructor<? extends T> constructor = this.usurpatedClass.getConstructor(this.constructorParameterTypes);
206                 valueObject = createInstance(valueObject, constructor);
207             } catch (NoSuchMethodException e) {
208                 throw new UsurperException("Can not instantiate bean:" + this.usurpatedClass.getName(), e);
209             }
210         } else {
211             Constructor<? extends T> constructor = choseConstructor(this.usurpatedClass);
212             valueObject = createInstance(valueObject, constructor);
213         }
214         return valueObject;
215     }
216 
217     @SuppressWarnings("unchecked")
218     private Constructor<? extends T> choseConstructor(Class<? extends T> usurpatedClass) {
219         Constructor<? extends T> result = null;
220         Set<Constructor> constructors = new TreeSet<Constructor>(new Comparator<Constructor>() {
221             public int compare(Constructor o1, Constructor o2) {
222                 int result = 0;
223                 result = (o1.getParameterTypes().length > o2.getParameterTypes().length) ? -1 : result;
224                 result = (o1.getParameterTypes().length < o2.getParameterTypes().length) ? 1 : result;
225                 return -result;
226             }
227         });
228         for (Constructor constructor : Arrays.asList(usurpatedClass.getConstructors())) {
229             constructors.add(constructor);
230         }
231         for (Constructor<? extends T> constructor : constructors) {
232             boolean constructorOk = true;
233             for (Class<?> parameterType : constructor.getParameterTypes()) {
234                 IHandler handler = getHandler(parameterType);
235                 if (handler == null) {
236                     constructorOk = false;
237                     break;
238                 }
239             }
240             if (constructorOk) {
241                 result = constructor;
242                 break;
243             }
244         }
245         return result;
246     }
247 
248     /**
249      * @param valueObject
250      * @param constructor
251      * @return
252      * @throws UsurperException
253      */
254     private T createInstance(T valueObject, Constructor<? extends T> constructor) throws UsurperException {
255         Class<?>[] parameterTypes = constructor.getParameterTypes();
256         Object[] constructorArguments = new Object[parameterTypes.length];
257         int argCounter = 0;
258         for (Class<?> parameterType : parameterTypes) {
259             IHandler handler = getHandler(parameterType);
260             if (handler == null) {
261                 break;
262             } else {
263                 HandledConstructorArg handledConstructorArg = new HandledConstructorArg(constructor, parameterType, ++argCounter, this.setup);
264                 constructorArguments[argCounter - 1] = handler.handle(handledConstructorArg);
265             }
266         }
267         if ((argCounter) == constructorArguments.length && constructorArguments.length == parameterTypes.length) {
268             try {
269                 valueObject = constructor.newInstance(constructorArguments);
270             } catch (Exception e) {
271                 throw new UsurperException("Can not instantiate bean:" + this.usurpatedClass.getName(), e);
272             }
273         }
274         return valueObject;
275     }
276 
277     private IHandler getHandler(Class<?> propertyType) {
278         return getHandler(null, propertyType);
279     }
280 
281     private IHandler getHandler(String propertyName, Class<?> propertyType) {
282         IHandler handler;
283         SpecificPropertyDefinition specificPropertyDefinition = new SpecificPropertyDefinition(this.usurpatedClass, propertyName);
284 
285         if (propertyType.isArray()) {
286             handler = this.setup.getArrayHandler();
287         } else if (propertyType.isEnum()) {
288             handler = this.setup.getEnumHandler();
289         } else if ((propertyName != null) && (this.setup.hasSpecificPropertyHandler(specificPropertyDefinition))) {
290             handler = this.setup.getSpecificPropertyHandler(specificPropertyDefinition);
291         } else {
292             handler = this.setup.getPropertyTypeHandler(new PropertyTypeDefinition(propertyType));
293         }
294 
295         return handler;
296     }
297 
298     /**
299      * Utility method that generates a list of usurpers of the type specified as Generic for the UsurperGenerator instance.
300      * 
301      * @param number
302      *            the quantity of usurpers to be generated
303      * @return a List of usurpers.
304      */
305     public List<T> generateUsurperList(int number) {
306         List<T> list = new ArrayList<T>();
307         for (int i = 0; i < number; i++) {
308             list.add(generateUsurper());
309         }
310         return list;
311     }
312 
313     /**
314      * Utility method that generates a list of usurpers of the type specified as Generic for the UsurperGenerator instance.
315      * 
316      * @param number
317      *            the quantity of usurpers to be generated
318      * @return a List of usurpers.
319      */
320     public Set<T> generateUsurperSet(int number) {
321         Set<T> set = new HashSet<T>();
322         for (int i = 0; i < number; i++) {
323             set.add(generateUsurper());
324         }
325         return set;
326     }
327 
328     /**
329      * This methods allows the replacement of the setup object.
330      * 
331      * @param usurperGeneratorSetup
332      */
333     public void replaceSetup(IUsurperGeneratorSetup usurperGeneratorSetup) {
334         ImmutableUsurperGeneratorSetup immutableUsurperGeneratorSetup = null;
335         if (usurperGeneratorSetup instanceof UsurperGeneratorSetup) {
336             immutableUsurperGeneratorSetup = ((UsurperGeneratorSetup) usurperGeneratorSetup).getImmutable();
337         } else if (usurperGeneratorSetup instanceof ImmutableUsurperGeneratorSetup) {
338             immutableUsurperGeneratorSetup = (ImmutableUsurperGeneratorSetup) usurperGeneratorSetup;
339         } else {
340             throw new IllegalArgumentException(usurperGeneratorSetup.getClass().getName() + " is not a valid setup instance. Valid implementations are:\n\t-" + UsurperGeneratorSetup.class.getName() + "\n\t-" + ImmutableUsurperGeneratorSetup.class.getName());
341         }
342         this.setup = immutableUsurperGeneratorSetup;
343     }
344 
345     /**
346      * This methods returns the current setup object
347      * 
348      * @return an immutable setup object
349      */
350     public ImmutableUsurperGeneratorSetup getSetup() {
351         return this.setup;
352     }
353 
354     /**
355      * Setter for the flag specifying if the undefined handlers must be ignored or trigger a failure.
356      * 
357      * @param onMissingHandlers
358      *            The value to set.
359      * @see OnMissingHandlers
360      * @deprecated prefer the use of the UsurperGeneratorSetup
361      * @see UsurperGeneratorSetup
362      */
363     public void onMissingHandlers(OnMissingHandlers onMissingHandlers) {
364         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
365         mutableSetup.onMissingHandlers(onMissingHandlers);
366         this.setup = mutableSetup.getImmutable();
367     }
368 
369     /**
370      * This method allows the user to register a Handler for Properties of determinated java types.<br>
371      * Only one handler can be registered for a given java type at a time.<br>
372      * Registering a handler when one is already registered for a given type overrides the preceding one.<br>
373      * 
374      * @param typeHandler
375      *            the handler to register (AbstractPropertyTypeHandler)
376      * @deprecated prefer the use of the UsurperGeneratorSetup
377      * @see UsurperGeneratorSetup
378      */
379     public void registerPropertyTypeHandler(AbstractPropertyTypeHandler typeHandler) {
380         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
381         mutableSetup.registerPropertyTypeHandler(typeHandler);
382         this.setup = mutableSetup.getImmutable();
383     }
384 
385     /**
386      * This method allows the user to register many Handlers for Properties of determinated java types.<br>
387      * Only one handler can be registered for a given java type at a time.<br>
388      * Registering a handler when one is already registered for a given type overrides the preceding one.<br>
389      * 
390      * @param typeHandlers
391      *            the handler to register (Set&lt;AbstractPropertyTypeHandler&gt;)
392      * @deprecated prefer the use of the UsurperGeneratorSetup
393      * @see UsurperGeneratorSetup
394      */
395     public void registerPropertyTypeHandlers(Set<AbstractPropertyTypeHandler> typeHandlers) {
396         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
397         mutableSetup.registerPropertyTypeHandlers(typeHandlers);
398         this.setup = mutableSetup.getImmutable();
399     }
400 
401     /**
402      * This method tests if a handler is registered for a given Java Type.
403      * 
404      * @param type
405      *            the java class of the type to test
406      * @return true if one is registered, false otherwise
407      * @deprecated prefer the use of the UsurperGeneratorSetup
408      * @see UsurperGeneratorSetup
409      */
410     public boolean hasPropertyTypeHandler(Class<?> type) {
411         return this.setup.hasPropertyTypeHandler(new PropertyTypeDefinition(type));
412     }
413 
414     /**
415      * This method retrieves the Handler for Properties of a given java type.
416      * 
417      * @param handledType
418      *            the java type
419      * @return the registered handler or null if none is defined.
420      * @deprecated prefer the use of the UsurperGeneratorSetup
421      * @see UsurperGeneratorSetup
422      */
423     public AbstractPropertyTypeHandler getPropertyTypeHandler(Class<?> handledType) {
424         return this.setup.getPropertyTypeHandler(new PropertyTypeDefinition(handledType));
425     }
426 
427     /**
428      * This method allows the user to register a Handler for a given property.<br>
429      * Only one handler can be registered for a given property at a time.<br>
430      * Registering a handler when one is already registered for a given property overrides the preceding one.<br>
431      * 
432      * @param propertyHandler
433      *            the handler to register (AbstractSpecificPropertyHandler)
434      * @deprecated prefer the use of the UsurperGeneratorSetup
435      * @see UsurperGeneratorSetup
436      */
437     public void registerSpecificPropertyHandler(AbstractSpecificPropertyHandler propertyHandler) {
438         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
439         mutableSetup.getSpecificPropertyHandlersMap().put(propertyHandler.getTargetProperty(), propertyHandler);
440         this.setup = mutableSetup.getImmutable();
441     }
442 
443     /**
444      * This method tests if a handler is registered for a given Property of a given Class.
445      * 
446      * @param usurpatedClass
447      * @param handledProperty
448      * @return true if one is registered, false otherwise
449      * @deprecated prefer the use of the UsurperGeneratorSetup
450      * @see UsurperGeneratorSetup
451      */
452     public boolean hasSpecificPropertyHandler(Class<?> usurpatedClass, String handledProperty) {
453         return this.setup.getSpecificPropertyHandlersMap().containsKey(usurpatedClass.getName() + "." + handledProperty);
454     }
455 
456     /**
457      * This method retrieves the Handler for a given Property of a given Class.
458      * 
459      * @param usurpatedClass
460      * @param handledProperty
461      * @return the registered property handler or null if none is defined.
462      * @deprecated prefer the use of the UsurperGeneratorSetup
463      * @see UsurperGeneratorSetup
464      */
465     public AbstractSpecificPropertyHandler getSpecificPropertyHandler(Class<?> usurpatedClass, String handledProperty) {
466         return this.setup.getSpecificPropertyHandlersMap().get(usurpatedClass.getName() + "." + handledProperty);
467     }
468 
469     /**
470      * This methods return all the handlers.
471      * 
472      * @return
473      * @deprecated prefer the use of the UsurperGeneratorSetup
474      * @see UsurperGeneratorSetup
475      */
476     public Map<ITargetDefinition, IHandler> getAllHandlers() {
477         return this.setup.getAllHandlers();
478     }
479 
480     /**
481      * This methods sets handlers. The Map must contains pairs of :
482      * <ul>
483      * <li>String,AbstractSpecificPropertyHandler
484      * <li>Class,AbstractPropertyTypeHandler
485      * </ul>
486      * Otherwise, it will throw a ClassCastException.
487      * 
488      * @param handlers
489      * @deprecated prefer the use of the UsurperGeneratorSetup
490      * @see UsurperGeneratorSetup
491      */
492     public void setAllHandlers(Map<ITargetDefinition, IHandler> handlers) {
493         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
494         mutableSetup.setAllHandlers(handlers);
495         this.setup = mutableSetup.getImmutable();
496     }
497 
498     /**
499      * @param propertyWritingMechanism
500      * @deprecated prefer the use of the UsurperGeneratorSetup
501      * @see UsurperGeneratorSetup
502      */
503     public void usePropertyWritingMechanism(PropertyWritingMechanism propertyWritingMechanism) {
504         UsurperGeneratorSetup mutableSetup = new UsurperGeneratorSetup(this.setup);
505         mutableSetup.usePropertyWritingMechanism(propertyWritingMechanism);
506         this.setup = mutableSetup.getImmutable();
507     }
508 
509     /**
510      * @param constructorParameterTypes
511      */
512     public void useConstructor(Class<?>... constructorParameterTypes) {
513         this.constructorParameterTypes = constructorParameterTypes;
514     }
515 
516 }