PodamFactoryImpl.java

  1. /**
  2.  *
  3.  */
  4. package uk.co.jemos.podam.api;

  5. import net.jcip.annotations.Immutable;
  6. import net.jcip.annotations.NotThreadSafe;

  7. import org.apache.commons.lang3.ArrayUtils;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;

  10. import uk.co.jemos.podam.api.DataProviderStrategy.Order;
  11. import uk.co.jemos.podam.common.AttributeStrategy;
  12. import uk.co.jemos.podam.common.Holder;
  13. import uk.co.jemos.podam.common.ManufacturingContext;
  14. import uk.co.jemos.podam.common.PodamConstants;
  15. import uk.co.jemos.podam.common.PodamConstructor;
  16. import uk.co.jemos.podam.exceptions.PodamMockeryException;
  17. import uk.co.jemos.podam.typeManufacturers.TypeManufacturerUtil;

  18. import java.lang.annotation.Annotation;
  19. import java.lang.reflect.*;
  20. import java.util.*;
  21. import java.util.concurrent.ConcurrentHashMap;
  22. import java.util.concurrent.atomic.AtomicReference;


  23. /**
  24.  * The PODAM factory implementation
  25.  *
  26.  * @author mtedone
  27.  *
  28.  * @since 1.0.0
  29.  *
  30.  */
  31. @NotThreadSafe
  32. @Immutable
  33. public class PodamFactoryImpl implements PodamFactory {

  34.     // ------------------->> Constants

  35.     private static final String RESOLVING_COLLECTION_EXCEPTION_STR = "An exception occurred while resolving the collection";

  36.     private static final String MAP_CREATION_EXCEPTION_STR = "An exception occurred while creating a Map object";

  37.     /** Application logger */
  38.     private static final Logger LOG = LoggerFactory.getLogger(PodamFactoryImpl.class);

  39.     // ------------------->> Instance / variables

  40.     /**
  41.      * External factory to delegate production this factory cannot handle
  42.      * <p>
  43.      * The default is {@link NullExternalFactory}.
  44.      * </p>
  45.      */
  46.     private PodamFactory externalFactory
  47.             = NullExternalFactory.getInstance();

  48.     /**
  49.      * The strategy to use to fill data.
  50.      * <p>
  51.      * The default is {@link RandomDataProviderStrategyImpl}.
  52.      * </p>
  53.      */
  54.     private DataProviderStrategy strategy
  55.             = new RandomDataProviderStrategyImpl();

  56.     /**
  57.      * The strategy to use to introspect data.
  58.      * <p>
  59.      * The default is {@link DefaultClassInfoStrategy}.
  60.      * </p>
  61.      */
  62.     private ClassInfoStrategy classInfoStrategy
  63.             = DefaultClassInfoStrategy.getInstance();

  64.     // ------------------->> Constructors

  65.     /**
  66.      * Default constructor.
  67.      */
  68.     public PodamFactoryImpl() {
  69.         this(NullExternalFactory.getInstance(),
  70.                 new RandomDataProviderStrategyImpl());
  71.     }

  72.     /**
  73.      * Constructor with non-default strategy
  74.      *
  75.      * @param strategy
  76.      *            The strategy to use to fill data
  77.      */
  78.     public PodamFactoryImpl(DataProviderStrategy strategy) {

  79.         this(NullExternalFactory.getInstance(), strategy);
  80.     }

  81.     /**
  82.      * Constructor with non-default external factory
  83.      *
  84.      * @param externalFactory
  85.      *            External factory to delegate production this factory cannot
  86.      *            handle
  87.      */
  88.     public PodamFactoryImpl(PodamFactory externalFactory) {
  89.         this(externalFactory, new RandomDataProviderStrategyImpl());
  90.     }

  91.     /**
  92.      * Full constructor.
  93.      *
  94.      * @param externalFactory
  95.      *            External factory to delegate production this factory cannot
  96.      *            handle
  97.      * @param strategy
  98.      *            The strategy to use to fill data
  99.      */
  100.     public PodamFactoryImpl(PodamFactory externalFactory,
  101.             DataProviderStrategy strategy) {
  102.         this.externalFactory = externalFactory;
  103.         this.strategy = strategy;
  104.     }

  105.     // ------------------->> Public methods

  106.     /**
  107.      * {@inheritDoc}
  108.      */
  109.     @Override
  110.     public <T> T manufacturePojoWithFullData(Class<T> pojoClass, Type... genericTypeArgs) {
  111.         ManufacturingContext manufacturingCtx = new ManufacturingContext();
  112.         manufacturingCtx.getPojos().put(pojoClass, 1);
  113.         manufacturingCtx.setConstructorOrdering(Order.HEAVY_FIRST);
  114.         return doManufacturePojo(pojoClass, manufacturingCtx, genericTypeArgs);
  115.     }

  116.     /**
  117.      * {@inheritDoc}
  118.      */
  119.     @Override
  120.     public <T> T manufacturePojo(Class<T> pojoClass, Type... genericTypeArgs) {
  121.         ManufacturingContext manufacturingCtx = new ManufacturingContext();
  122.         manufacturingCtx.getPojos().put(pojoClass, 1);
  123.         return doManufacturePojo(pojoClass, manufacturingCtx, genericTypeArgs);
  124.     }

  125.     /**
  126.      * {@inheritDoc}
  127.      */
  128.     @Override
  129.     public <T> T populatePojo(T pojo, Type... genericTypeArgs) {
  130.         ManufacturingContext manufacturingCtx = new ManufacturingContext();
  131.         manufacturingCtx.getPojos().put(pojo.getClass(), 1);
  132.         Type[] genericTypeArgsExtra = ManufacturingContext.fillTypeArgsMap(
  133.                 manufacturingCtx, pojo.getClass(), genericTypeArgs);
  134.         try {
  135.             List<Annotation> annotations = null;
  136.             return this.populatePojoInternal(pojo, annotations,
  137.                     manufacturingCtx, genericTypeArgsExtra);
  138.         } catch (InstantiationException e) {
  139.             throw new PodamMockeryException(e.getMessage(), e);
  140.         } catch (IllegalAccessException e) {
  141.             throw new PodamMockeryException(e.getMessage(), e);
  142.         } catch (InvocationTargetException e) {
  143.             throw new PodamMockeryException(e.getMessage(), e);
  144.         } catch (ClassNotFoundException e) {
  145.             throw new PodamMockeryException(e.getMessage(), e);
  146.         }
  147.     }

  148.     // ------------------->> Getters / Setters

  149.     /**
  150.      * {@inheritDoc}
  151.      */
  152.     @Override
  153.     public DataProviderStrategy getStrategy() {
  154.         return strategy;
  155.     }

  156.     /**
  157.      * {@inheritDoc}
  158.      */
  159.     @Override
  160.     public PodamFactory setStrategy(DataProviderStrategy strategy) {
  161.         this.strategy = strategy;
  162.         return this;
  163.     }

  164.     /**
  165.      * {@inheritDoc}
  166.      */
  167.     @Override
  168.     public ClassInfoStrategy getClassStrategy() {
  169.         return classInfoStrategy;
  170.     }

  171.     /**
  172.      * {@inheritDoc}
  173.      */
  174.     @Override
  175.     public PodamFactory setClassStrategy(ClassInfoStrategy classInfoStrategy) {
  176.         this.classInfoStrategy = classInfoStrategy;
  177.         return this;
  178.     }

  179.     /**
  180.      * {@inheritDoc}
  181.      */
  182.     @Override
  183.     public PodamFactory getExternalFactory() {
  184.         return externalFactory;
  185.     }

  186.     /**
  187.      * {@inheritDoc}
  188.      */
  189.     @Override
  190.     public PodamFactory setExternalFactory(PodamFactory externalFactory) {
  191.         this.externalFactory = externalFactory;
  192.         return this;
  193.     }

  194.     // ------------------->> Private methods

  195.     /**
  196.      * It attempts to create an instance of the given class with a static method
  197.      * of the factory
  198.      * <p>
  199.      * This method attempts to instantiate POJO with a static method of provided
  200.      * factory, for example, getInstance().
  201.      * </p>
  202.      *
  203.      * @param <T>
  204.      *            The type of Pojo class
  205.      * @param factoryClass
  206.      *            The factory class, which will be used for POJO instantiation
  207.      * @param pojoClass
  208.      *            The name of the class for which an instance filled with values
  209.      * @param manufacturingCtx
  210.      *            the manufacturing context
  211.      * @param genericTypeArgs
  212.      *            The generic type arguments for the current generic class
  213.      *            instance
  214.      *
  215.      *
  216.      * @return An instance of the given class
  217.      * @throws IllegalArgumentException
  218.      *             If an illegal argument was passed to the constructor
  219.      * @throws InstantiationException
  220.      *             If an exception occurred during instantiation
  221.      * @throws IllegalAccessException
  222.      *             If security was violated while creating the object
  223.      * @throws InvocationTargetException
  224.      *             If an exception occurred while invoking the constructor or
  225.      *             factory method
  226.      * @throws ClassNotFoundException
  227.      *             If it was not possible to create a class from a string
  228.      */
  229.     private <T> T instantiatePojoWithFactory(
  230.             Class<?> factoryClass, Class<T> pojoClass,
  231.             ManufacturingContext manufacturingCtx,
  232.             Type... genericTypeArgs)
  233.             throws InstantiationException, IllegalAccessException,
  234.             InvocationTargetException, ClassNotFoundException {

  235.         // If no publicly accessible constructors are available,
  236.         // the best we can do is to find a constructor (e.g.
  237.         // getInstance())

  238.         Method[] declaredMethods = TypeManufacturerUtil.findSuitableConstructors(
  239.                 factoryClass, pojoClass);
  240.         strategy.sort(declaredMethods, manufacturingCtx.getConstructorOrdering());

  241.         // A candidate factory method is a method which returns the
  242.         // Class type

  243.         // The parameters to pass to the method invocation
  244.         Object[] parameterValues = null;

  245.         for (Method candidateConstructor : declaredMethods) {

  246.             Object factoryInstance = null;
  247.             if (!Modifier.isStatic(candidateConstructor.getModifiers())) {
  248.                 factoryInstance = manufacturePojo(factoryClass);
  249.             }

  250.             parameterValues = getParameterValuesForMethod(candidateConstructor,
  251.                     pojoClass, manufacturingCtx, genericTypeArgs);

  252.             try {

  253.                 @SuppressWarnings("unchecked")
  254.                 T retValue = (T) candidateConstructor.invoke(factoryInstance,
  255.                         parameterValues);
  256.                 LOG.debug("Could create an instance using "
  257.                         + candidateConstructor);
  258.                 return retValue;

  259.             } catch (Exception t) {

  260.                 LOG.debug(
  261.                         "PODAM could not create an instance for constructor: "
  262.                                 + candidateConstructor
  263.                                 + ". Will try another one...", t);

  264.             }

  265.         }

  266.         LOG.debug("For class {} PODAM could not possibly create"
  267.                 + " a value statically. Will try other means.",
  268.                 pojoClass);
  269.         return null;

  270.     }

  271.     /**
  272.      * It creates and returns an instance of the given class if at least one of
  273.      * its constructors has been annotated with {@link PodamConstructor}
  274.      *
  275.      * @param <T>
  276.      *            The type of the instance to return
  277.      *
  278.      * @param pojoClass
  279.      *            The class of which an instance is required
  280.      * @param manufacturingCtx
  281.      *            the manufacturing context
  282.      * @param genericTypeArgs
  283.      *            The generic type arguments for the current generic class
  284.      *            instance
  285.      * @return an instance of the given class if at least one of its
  286.      *         constructors has been annotated with {@link PodamConstructor}
  287.      * @throws SecurityException
  288.      *             If an security was violated
  289.      */
  290.     private <T> T instantiatePojo(Class<T> pojoClass,
  291.             ManufacturingContext manufacturingCtx,
  292.             Type... genericTypeArgs)
  293.             throws SecurityException {

  294.         T retValue = null;

  295.         Constructor<?>[] constructors = pojoClass.getConstructors();
  296.         if (constructors.length == 0 || Modifier.isAbstract(pojoClass.getModifiers())) {
  297.             /* No public constructors, we will try static factory methods */
  298.             try {
  299.                 retValue = instantiatePojoWithFactory(
  300.                         pojoClass, pojoClass, manufacturingCtx, genericTypeArgs);
  301.             } catch (Exception e) {
  302.                 LOG.debug("We couldn't create an instance for pojo: "
  303.                         + pojoClass + " with factory methods, will "
  304.                         + " try non-public constructors.", e);
  305.             }

  306.             /* Then non-public constructors */
  307.             if (retValue == null) {
  308.                 constructors = pojoClass.getDeclaredConstructors();
  309.             }
  310.         }

  311.         if (retValue == null) {

  312.             strategy.sort(constructors, manufacturingCtx.getConstructorOrdering());

  313.             for (Constructor<?> constructor : constructors) {

  314.                 try {
  315.                     Object[] parameterValues = getParameterValuesForConstructor(
  316.                             constructor, pojoClass, manufacturingCtx,
  317.                             genericTypeArgs);

  318.                     // Security hack
  319.                     if (!constructor.isAccessible()) {
  320.                         constructor.setAccessible(true);
  321.                     }

  322.                     @SuppressWarnings("unchecked")
  323.                     T tmp = (T) constructor.newInstance(parameterValues);
  324.                     retValue = tmp;
  325.                     LOG.debug("We could create an instance with constructor: "
  326.                             + constructor);
  327.                     break;
  328.                 } catch (Exception e) {
  329.                     LOG.debug("We couldn't create an instance for pojo: {} with"
  330.                             + " constructor: {}. Will try with another one.",
  331.                             pojoClass, constructor, e);
  332.                 }
  333.             }
  334.         }

  335.         if (retValue == null) {
  336.             LOG.debug("For class {} PODAM could not possibly create"
  337.                     + " a value. Will try other means.", pojoClass);
  338.         }
  339.         return retValue;
  340.     }

  341.     /**
  342.      * Manufactures and populates the pojo class
  343.      *
  344.      * @param <T> The type of the instance to return
  345.      * @param pojoClass the class to instantiate
  346.      * @param manufacturingCtx the initialized manufacturing context
  347.      * @param genericTypeArgs generic arguments for the pojo class
  348.      * @return instance of @pojoClass or null in case it cannot be instantiated
  349.      */
  350.     private <T> T doManufacturePojo(Class<T> pojoClass,
  351.             ManufacturingContext manufacturingCtx, Type... genericTypeArgs) {
  352.         try {
  353.             Class<?> declaringClass = null;
  354.             Object declaringInstance = null;
  355.             AttributeMetadata pojoMetadata = new AttributeMetadata(pojoClass,
  356.                     pojoClass, genericTypeArgs, declaringClass, declaringInstance);
  357.             return this.manufacturePojoInternal(pojoClass, pojoMetadata,
  358.                     manufacturingCtx, genericTypeArgs);
  359.         } catch (InstantiationException e) {
  360.             throw new PodamMockeryException(e.getMessage(), e);
  361.         } catch (IllegalAccessException e) {
  362.             throw new PodamMockeryException(e.getMessage(), e);
  363.         } catch (InvocationTargetException e) {
  364.             throw new PodamMockeryException(e.getMessage(), e);
  365.         } catch (ClassNotFoundException e) {
  366.             throw new PodamMockeryException(e.getMessage(), e);
  367.         }
  368.     }

  369.     /**
  370.      * Generic method which returns an instance of the given class filled with
  371.      * values dictated by the strategy
  372.      *
  373.      * @param <T>
  374.      *            The type for which a filled instance is required
  375.      *
  376.      * @param pojoClass
  377.      *            The name of the class for which an instance filled with values
  378.      *            is required
  379.      * @param pojoMetadata
  380.      *            attribute metadata for POJOs produced recursively
  381.      * @param manufacturingCtx
  382.      *            the manufacturing context
  383.      * @param genericTypeArgs
  384.      *            The generic type arguments for the current generic class
  385.      *            instance
  386.      * @return An instance of &lt;T&gt; filled with dummy values
  387.      * @throws InstantiationException
  388.      *             If an exception occurred during instantiation
  389.      * @throws IllegalAccessException
  390.      *             If security was violated while creating the object
  391.      * @throws InvocationTargetException
  392.      *             If an exception occurred while invoking the constructor or
  393.      *             factory method
  394.      * @throws ClassNotFoundException
  395.      *             If manufactured class cannot be loaded
  396.      * @throws PodamMockeryException
  397.      *             if a problem occurred while creating a POJO instance or while
  398.      *             setting its state
  399.      */
  400.     private <T> T manufacturePojoInternal(Class<T> pojoClass,
  401.             AttributeMetadata pojoMetadata, ManufacturingContext manufacturingCtx,
  402.             Type... genericTypeArgs)
  403.             throws InstantiationException, IllegalAccessException,
  404.             InvocationTargetException, ClassNotFoundException {

  405.         // reuse object from memoization table
  406.         @SuppressWarnings("unchecked")
  407.         T objectToReuse = (T) strategy.getMemoizedObject(pojoMetadata);
  408.         if (objectToReuse != null) {
  409.             LOG.debug("Fetched memoized object for {} with parameters {}",
  410.                     pojoClass, Arrays.toString(genericTypeArgs));
  411.             return objectToReuse;
  412.         } else {
  413.             LOG.debug("Manufacturing {} with parameters {}",
  414.                     pojoClass, Arrays.toString(genericTypeArgs));
  415.         }

  416.         manufacturingCtx.backupTypeArgsMap(manufacturingCtx.createEmptyTypeArgsMap());
  417.         Type[] genericTypeArgsExtra = ManufacturingContext.fillTypeArgsMap(
  418.                 manufacturingCtx, pojoClass, genericTypeArgs);

  419.         T retValue = (T) strategy.getTypeValue(pojoMetadata, manufacturingCtx, pojoClass);
  420.         if (null == retValue && !pojoClass.isInterface()) {

  421.             try {

  422.                 retValue = instantiatePojo(pojoClass, manufacturingCtx,
  423.                         genericTypeArgsExtra);
  424.             } catch (SecurityException e) {

  425.                 throw new PodamMockeryException(
  426.                         "Security exception while applying introspection.", e);
  427.             }
  428.         }

  429.         if (retValue == null) {
  430.             retValue = getValueForAbstractType(pojoClass, pojoMetadata,
  431.                     manufacturingCtx, genericTypeArgs);
  432.         } else {

  433.             // update memoization cache with new object
  434.             // the reference is stored before properties are set so that recursive
  435.             // properties can use it
  436.             strategy.cacheMemoizedObject(pojoMetadata, retValue);

  437.             List<Annotation> annotations = null;
  438.             populatePojoInternal(retValue, annotations, manufacturingCtx,
  439.                     genericTypeArgsExtra);
  440.         }

  441.         manufacturingCtx.restoreTypeArgsMap();
  442.         return retValue;
  443.     }



  444.     /**
  445.      * Fills given class filled with values dictated by the strategy
  446.      *
  447.      * @param <T>
  448.      *            The type for which should be populated
  449.      * @param pojo
  450.      *            An instance to be filled with dummy values
  451.      * @param annotations
  452.      *            a list of annotations attached to this POJO defined elsewhere
  453.      * @param manufacturingCtx
  454.      *            the manufacturing context
  455.      * @param genericTypeArgs
  456.      *            The generic type arguments for the current generic class
  457.      *            instance
  458.      * @return An instance of &lt;T&gt; filled with dummy values
  459.      * @throws InstantiationException
  460.      *             If an exception occurred during instantiation
  461.      * @throws IllegalAccessException
  462.      *             If security was violated while creating the object
  463.      * @throws InvocationTargetException
  464.      *             If an exception occurred while invoking the constructor or
  465.      *             factory method
  466.      * @throws ClassNotFoundException
  467.      *             If manufactured class cannot be loaded
  468.      */
  469.     private <T> T populatePojoInternal(T pojo, List<Annotation> annotations,
  470.             ManufacturingContext manufacturingCtx,
  471.             Type... genericTypeArgs)
  472.             throws InstantiationException, IllegalAccessException,
  473.             InvocationTargetException, ClassNotFoundException {

  474.         LOG.debug("Populating pojo {}", pojo.getClass());

  475.         Class<?> pojoClass = pojo.getClass();
  476.         if (pojoClass.isArray()) {
  477.             if (null == annotations) {
  478.                 annotations = new ArrayList<Annotation>();
  479.             }
  480.             String attributeName = null;
  481.             fillArray(pojo, attributeName,
  482.                     pojoClass.getClass().getComponentType(),
  483.                     pojoClass.getClass().getComponentType(),
  484.                     annotations,
  485.                     manufacturingCtx);
  486.         } else if (pojo instanceof Collection) {
  487.             @SuppressWarnings("unchecked")
  488.             Collection<Object> collection = (Collection<Object>) pojo;
  489.             AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
  490.                     PodamConstants.NO_TYPES);
  491.             Class<?> elementTypeClass = findInheretedCollectionElementType(collection,
  492.                     manufacturingCtx, elementGenericTypeArgs, genericTypeArgs);
  493.             if (null == annotations) {
  494.                 annotations = new ArrayList<Annotation>();
  495.             }
  496.             for (Annotation annotation : collection.getClass().getAnnotations()) {
  497.                 annotations.add(annotation);
  498.             }
  499.             String attributeName = null;
  500.             fillCollection(manufacturingCtx, annotations, attributeName,
  501.                     collection, elementTypeClass, elementGenericTypeArgs.get());
  502.         } else if (pojo instanceof Map) {
  503.             @SuppressWarnings("unchecked")
  504.             Map<Object,Object> map = (Map<Object,Object>)pojo;
  505.             MapArguments mapArguments = findInheretedMapElementType(
  506.                     map, manufacturingCtx, genericTypeArgs);
  507.             if (null != annotations) {
  508.                 mapArguments.getAnnotations().addAll(annotations);
  509.             }
  510.             fillMap(mapArguments, manufacturingCtx);
  511.         }

  512.         ClassInfo classInfo = classInfoStrategy.getClassInfo(pojo.getClass());

  513.         Set<ClassAttribute> classAttributes = classInfo.getClassAttributes();

  514.         for (ClassAttribute attribute : classAttributes) {

  515.             if (!populateReadWriteField(pojo, attribute, manufacturingCtx)) {
  516.                 populateReadOnlyField(pojo, attribute, manufacturingCtx, genericTypeArgs);
  517.             }
  518.         }

  519.         // It executes any extra methods
  520.         Collection<Method> extraMethods = classInfoStrategy.getExtraMethods(pojoClass);
  521.         if (null != extraMethods) {
  522.             for (Method extraMethod : extraMethods) {

  523.                 Object[] args = getParameterValuesForMethod(extraMethod, pojoClass,
  524.                         manufacturingCtx, genericTypeArgs);
  525.                 extraMethod.invoke(pojo, args);
  526.             }
  527.         }

  528.         return pojo;
  529.     }

  530.     /**
  531.      * Fills a field with a getter
  532.      *
  533.      * @param <T>
  534.      *            The type for which should be populated
  535.      * @param pojo
  536.      *            The POJO being filled with values
  537.      * @param attribute
  538.      *            a attribute we are filling
  539.      * @param manufacturingCtx
  540.      *            the manufacturing context
  541.      * @param genericTypeArgs
  542.      *            The generic type arguments for the current generic class
  543.      *            instance
  544.      * @return true, if attribute was found and populated
  545.      * @throws ClassNotFoundException
  546.      *              If class being manufactured cannot be loaded
  547.      * @throws InstantiationException
  548.      *             If an exception occurred during instantiation
  549.      * @throws IllegalAccessException
  550.      *             If security was violated while creating the object
  551.      * @throws InvocationTargetException
  552.      *             If an exception occurred while invoking the constructor or
  553.      *             factory method
  554.      */
  555.     private <T> boolean populateReadOnlyField(T pojo, ClassAttribute attribute,
  556.             ManufacturingContext manufacturingCtx,
  557.             Type... genericTypeArgs)
  558.             throws InstantiationException, IllegalAccessException,
  559.                     InvocationTargetException, ClassNotFoundException {

  560.         Method getter = PodamUtils.selectLatestMethod(attribute.getGetters());
  561.         if (getter == null) {
  562.             return false;
  563.         }

  564.         if (getter.getGenericParameterTypes().length > 0) {
  565.             LOG.warn("Skipping invalid getter {}", getter);
  566.             return false;
  567.         }

  568.         Class<?> pojoType = getter.getReturnType();
  569.         if (pojoType.isPrimitive()) {
  570.             /* TODO: non-zero values should be fine */
  571.             return false;
  572.         }

  573.         Object fieldValue = null;
  574.         try {
  575.             fieldValue = getter.invoke(pojo, PodamConstants.NO_ARGS);
  576.         } catch(Exception e) {
  577.             LOG.debug("Cannot access {}, skipping", getter);
  578.         }

  579.         if (fieldValue != null) {

  580.             LOG.debug("Populating read-only field {}", getter);

  581.             Class<?> fieldClass = fieldValue.getClass();
  582.             Integer depth = manufacturingCtx.getPojos().get(fieldClass);
  583.             if (depth == null) {
  584.                 depth = 0;
  585.             }
  586.             if (depth < strategy.getMaxDepth(fieldClass)) {

  587.                 Type[] genericTypeArgsAll;
  588.                 Type genericPojoType = getter.getGenericReturnType();
  589.                 final boolean cloneTypeArgsMap = (genericPojoType instanceof ParameterizedType);
  590.                 if (cloneTypeArgsMap) {

  591.                     genericTypeArgsAll = manufacturingCtx.cloneTypeArgsMap(
  592.                             pojoType, (ParameterizedType) genericPojoType, genericTypeArgs);
  593.                 } else {

  594.                     genericTypeArgsAll = genericTypeArgs;
  595.                 }

  596.                 List<Annotation> pojoAttributeAnnotations =
  597.                         PodamUtils.getAttributeAnnotations(
  598.                                 attribute.getAttribute(), getter);

  599.                 manufacturingCtx.getPojos().put(fieldClass, depth + 1);
  600.                 populatePojoInternal(fieldValue, pojoAttributeAnnotations,
  601.                         manufacturingCtx, genericTypeArgsAll);
  602.                 manufacturingCtx.getPojos().put(fieldClass, depth);
  603.                 if (cloneTypeArgsMap) {
  604.                     manufacturingCtx.restoreTypeArgsMap();
  605.                 }
  606.             } else {

  607.                 LOG.warn("Loop of depth " + depth + " in filling read-only field {} detected.",
  608.                         getter);
  609.             }
  610.             return true;
  611.         } else {

  612.             return false;
  613.         }
  614.     }

  615.     /**
  616.      * Fills a field with a setter
  617.      *
  618.      * @param <T>
  619.      *            The type for which should be populated
  620.      * @param pojo
  621.      *            The POJO being filled with values
  622.      * @param attribute
  623.      *            a attribute we are filling
  624.      * @param manufacturingCtx
  625.      *            the manufacturing context
  626.      * @return true, if attribute was found and populated
  627.      * @throws ClassNotFoundException
  628.      *              If class being manufactured cannot be loaded
  629.      * @throws InstantiationException
  630.      *             If an exception occurred during instantiation
  631.      * @throws IllegalAccessException
  632.      *             If security was violated while creating the object
  633.      * @throws InvocationTargetException
  634.      *             If an exception occurred while invoking the constructor or
  635.      *             factory method
  636.      */
  637.     private <T> boolean populateReadWriteField(T pojo, ClassAttribute attribute,
  638.             ManufacturingContext manufacturingCtx)
  639.             throws InstantiationException, IllegalAccessException,
  640.                     InvocationTargetException, ClassNotFoundException {

  641.         Method setter = PodamUtils.selectLatestMethod(attribute.getSetters());
  642.         if (setter == null) {
  643.             return false;
  644.         }

  645.         Class<?>[] parameterTypes = setter.getParameterTypes();
  646.         if (parameterTypes.length != 1) {
  647.             // According to JavaBeans standards, setters should have only
  648.             // one argument
  649.             LOG.warn("Skipping setter with non-single arguments {}",
  650.                     setter);
  651.             return false;
  652.         }

  653.         LOG.debug("Populating read-write field {}", setter);

  654.         // A class which has got an attribute to itself (e.g.
  655.         // recursive hierarchies)
  656.         Class<?> attributeType = parameterTypes[0];

  657.         // If an attribute has been annotated with
  658.         // PodamAttributeStrategy, it takes the precedence over any
  659.         // other strategy. Additionally we don't pass the attribute
  660.         // metadata for value customisation; if user went to the extent
  661.         // of specifying a PodamAttributeStrategy annotation for an
  662.         // attribute they are already customising the value assigned to
  663.         // that attribute.

  664.         List<Annotation> pojoAttributeAnnotations
  665.                 = PodamUtils.getAttributeAnnotations(
  666.                         attribute.getAttribute(), setter);

  667.         AttributeStrategy<?> attributeStrategy
  668.                 = TypeManufacturerUtil.findAttributeStrategy(strategy, pojoAttributeAnnotations, attributeType);
  669.         if (null == attributeStrategy) {
  670.             attributeStrategy = strategy.getStrategyForAttribute(attribute);
  671.         }
  672.         Object setterArg = null;
  673.         if (null != attributeStrategy) {

  674.             setterArg = TypeManufacturerUtil.returnAttributeDataStrategyValue(
  675.                     attributeType, pojoAttributeAnnotations, attributeStrategy);

  676.         } else {

  677.             AtomicReference<Type[]> typeGenericTypeArgs
  678.                     = new AtomicReference<Type[]>(PodamConstants.NO_TYPES);
  679.             // If the parameter is a generic parameterized type resolve
  680.             // the actual type arguments
  681.             Type genericType = setter.getGenericParameterTypes()[0];

  682.             final Type[] typeArguments;
  683.             if (!(genericType instanceof GenericArrayType)) {
  684.                 attributeType = TypeManufacturerUtil.resolveGenericParameter(genericType,
  685.                         manufacturingCtx, typeGenericTypeArgs);
  686.                 typeArguments = typeGenericTypeArgs.get();
  687.             } else {
  688.                 typeArguments = PodamConstants.NO_TYPES;
  689.             }

  690.             if (!Collection.class.isAssignableFrom(attributeType) && !Map.class.isAssignableFrom(attributeType)) {
  691.                 for (int i = 0; i < typeArguments.length; i++) {
  692.                     if (typeArguments[i] instanceof TypeVariable) {
  693.                         Class<?> resolvedType = TypeManufacturerUtil.resolveGenericParameter(typeArguments[i],
  694.                                 manufacturingCtx, typeGenericTypeArgs);
  695.                         typeArguments[i] = resolvedType;
  696.                     }
  697.                 }
  698.             }

  699.             setterArg = manufactureAttributeValue(pojo, manufacturingCtx,
  700.                     attributeType, genericType,
  701.                     pojoAttributeAnnotations, attribute.getName(),
  702.                     typeArguments);
  703.         }

  704.         try {
  705.             setter.invoke(pojo, setterArg);
  706.         } catch(IllegalAccessException e) {
  707.             LOG.warn("{} is not accessible. Setting it to accessible."
  708.                     + " However this is a security hack and your code"
  709.                     + " should really adhere to JavaBeans standards.",
  710.                     setter.toString());
  711.             setter.setAccessible(true);
  712.             setter.invoke(pojo, setterArg);
  713.         }
  714.         return true;
  715.     }

  716.     /**
  717.      * It manufactures and returns the value for a POJO attribute.
  718.      *
  719.      *
  720.      * @param pojo
  721.      *            The POJO being filled with values
  722.      * @param manufacturingCtx
  723.      *            the manufacturing context
  724.      * @param attributeType
  725.      *            The type of the attribute for which a value is being
  726.      *            manufactured
  727.      * @param genericAttributeType
  728.      *            The generic type of the attribute for which a value is being
  729.      *            manufactured
  730.      * @param annotations
  731.      *            The annotations for the attribute being considered
  732.      * @param attributeName
  733.      *            The attribute name
  734.      * @param genericTypeArgs
  735.      *            The generic type arguments for the current generic class
  736.      *            instance
  737.      * @return The value for an attribute
  738.      *
  739.      * @throws InstantiationException
  740.      *             If an exception occurred during instantiation
  741.      * @throws IllegalAccessException
  742.      *             If security was violated while creating the object
  743.      * @throws InvocationTargetException
  744.      *             If an exception occurred while invoking the constructor or
  745.      *             factory method
  746.      * @throws IllegalArgumentException
  747.      *             <ul>
  748.      *             <li>If an illegal argument was passed</li>
  749.      *             <li>If an invalid value was set for a precise value in an
  750.      *             annotation and such value could not be converted to the
  751.      *             desired type</li>
  752.      *             </ul>
  753.      * @throws ClassNotFoundException
  754.      *              If class being manufactured cannot be loaded
  755.      *
  756.      */
  757.     private Object manufactureAttributeValue(Object pojo,
  758.             ManufacturingContext manufacturingCtx, Class<?> attributeType,
  759.             Type genericAttributeType, List<Annotation> annotations,
  760.             String attributeName,
  761.             Type... genericTypeArgs)
  762.             throws InstantiationException, IllegalAccessException,
  763.             InvocationTargetException, ClassNotFoundException {

  764.         Object attributeValue = null;

  765.         Class<?> pojoClass = (pojo instanceof Class ? (Class<?>) pojo : pojo.getClass());
  766.         Class<?> realAttributeType;
  767.         AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(PodamConstants.NO_TYPES);
  768.         if (attributeType != genericAttributeType
  769.                 && Object.class.equals(attributeType)
  770.                 && genericAttributeType instanceof TypeVariable) {
  771.             realAttributeType = TypeManufacturerUtil.resolveGenericParameter(genericAttributeType,
  772.                     manufacturingCtx, elementGenericTypeArgs);
  773.         } else {
  774.             realAttributeType = attributeType;
  775.         }

  776.         Type[] genericTypeArgsAll;
  777.         if (elementGenericTypeArgs.get().length > 0) {
  778.             genericTypeArgsAll = ArrayUtils.addAll(elementGenericTypeArgs.get(), genericTypeArgs);
  779.         } else {
  780.             genericTypeArgsAll = genericTypeArgs;
  781.         }
  782.         genericTypeArgsAll = TypeManufacturerUtil.mergeActualAndSuppliedGenericTypes(
  783.                     attributeType, genericAttributeType, genericTypeArgsAll, manufacturingCtx);

  784.         AttributeMetadata attributeMetadata = new AttributeMetadata(
  785.                 attributeName, realAttributeType, genericAttributeType,
  786.                 genericTypeArgsAll, annotations, pojoClass, pojo);

  787.         if (realAttributeType.isArray()) {

  788.             // Array type

  789.             attributeValue = resolveArrayElementValue(pojo, manufacturingCtx,
  790.                     attributeMetadata);

  791.             // Collection
  792.         } else if (Collection.class.isAssignableFrom(realAttributeType)) {

  793.             attributeValue = resolveCollectionValueWhenCollectionIsPojoAttribute(
  794.                     pojo, manufacturingCtx, attributeMetadata);

  795.             // Map
  796.         } else if (Map.class.isAssignableFrom(realAttributeType)) {

  797.             attributeValue = resolveMapValueWhenMapIsPojoAttribute(pojo,
  798.                     manufacturingCtx, attributeMetadata);

  799.         }

  800.         // For any other type, we use the PODAM strategy
  801.         if (attributeValue == null) {

  802.             Integer depth = manufacturingCtx.getPojos().get(realAttributeType);
  803.             if (depth == null) {
  804.                 depth = 0;
  805.             }
  806.             if (depth < strategy.getMaxDepth(pojoClass)) {

  807.                 manufacturingCtx.getPojos().put(realAttributeType, depth + 1);

  808.                 attributeValue = this.manufacturePojoInternal(
  809.                         realAttributeType, attributeMetadata, manufacturingCtx, genericTypeArgsAll);
  810.                 manufacturingCtx.getPojos().put(realAttributeType, depth);

  811.             } else {

  812.                 attributeValue = resortToExternalFactory(manufacturingCtx,
  813.                         "Loop of depth " +  depth + " in {} production detected. Resorting to {} external factory",
  814.                         realAttributeType, genericTypeArgsAll);

  815.             }
  816.         }

  817.         return attributeValue;
  818.     }

  819.     /**
  820.      * Delegates POJO manufacturing to an external factory
  821.      *
  822.      * @param <T>
  823.      *            The type of the instance to return
  824.      * @param manufacturingCtx
  825.      *            the manufacturing context
  826.      * @param msg
  827.      *            Message to log, must contain two parameters
  828.      * @param pojoClass
  829.      *            The class of which an instance is required
  830.      * @param genericTypeArgs
  831.      *            The generic type arguments for the current generic class
  832.      *            instance
  833.      * @return instance of POJO produced by external factory or null
  834.      */
  835.     private <T> T resortToExternalFactory(ManufacturingContext manufacturingCtx,
  836.             String msg, Class<T> pojoClass,
  837.             Type... genericTypeArgs) {

  838.         LOG.warn(msg, pojoClass, externalFactory.getClass().getName());
  839.         if (manufacturingCtx.getConstructorOrdering() == Order.HEAVY_FIRST) {
  840.             return externalFactory.manufacturePojoWithFullData(pojoClass, genericTypeArgs);
  841.         } else {
  842.             return externalFactory.manufacturePojo(pojoClass, genericTypeArgs);
  843.         }
  844.     }




  845.     /**
  846.      * It returns a collection of some sort with some data in it.
  847.      *
  848.      *
  849.      * @param pojo
  850.      *            The POJO being analyzed
  851.      * @param manufacturingCtx
  852.      *            the manufacturing context
  853.      * @param attributeMetadata
  854.      *            The attribute's metadata
  855.      * @return a collection of some sort with some data in it
  856.      * @throws PodamMockeryException
  857.      *             An exception occurred while resolving the collection
  858.      * @throws IllegalArgumentException
  859.      *             If the field name is null or empty
  860.      */
  861.     private Collection<? super Object> resolveCollectionValueWhenCollectionIsPojoAttribute(
  862.             Object pojo, ManufacturingContext manufacturingCtx,
  863.             AttributeMetadata attributeMetadata) {

  864.         String attributeName = attributeMetadata.getAttributeName();

  865.         // This needs to be generic because collections can be of any type
  866.         Collection<Object> defaultValue = null;
  867.         if (null != pojo && null != attributeName && !Character.isDigit(attributeName.charAt(0))) {

  868.             Object fieldValue = PodamUtils.getFieldValue(pojo, attributeName);
  869.             if (null != fieldValue) {
  870.                 if (!(fieldValue instanceof Collection)) {
  871.                     fieldValue = PodamUtils.getFieldValueWithGetter(pojo, attributeName);
  872.                 }

  873.                 if (fieldValue instanceof Collection) {
  874.                     @SuppressWarnings("unchecked")
  875.                     Collection<Object> tmp = (Collection<Object>)fieldValue;
  876.                     defaultValue = tmp;
  877.                 } else if (null != fieldValue) {
  878.                     LOG.warn("Obtained non-collection field default value {}", fieldValue.getClass());
  879.                 }
  880.             }
  881.         }

  882.         Collection<Object> retValue = null;
  883.         if (null != defaultValue &&
  884.                 (defaultValue.getClass().getModifiers() & Modifier.PRIVATE) == 0) {
  885.             /* Default collection, which is not immutable */
  886.             retValue = defaultValue;
  887.         } else {

  888.             @SuppressWarnings("unchecked")
  889.             Class<Collection<Object>> collectionType
  890.                     = (Class<Collection<Object>>) attributeMetadata.getAttributeType();
  891.             retValue = strategy.getTypeValue(attributeMetadata,
  892.                     manufacturingCtx, collectionType);
  893.             if (null != retValue && null != defaultValue) {
  894.                 retValue.addAll(defaultValue);
  895.             }
  896.         }

  897.         if (null == retValue) {
  898.             return null;
  899.         }

  900.         try {

  901.             Class<?> typeClass = null;

  902.             AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
  903.                     PodamConstants.NO_TYPES);
  904.             if (ArrayUtils.isEmpty(attributeMetadata.getAttrGenericArgs())) {

  905.                 typeClass = findInheretedCollectionElementType(retValue,
  906.                         manufacturingCtx, elementGenericTypeArgs, attributeMetadata.getAttrGenericArgs());
  907.             } else {
  908.                 Type actualTypeArgument = attributeMetadata.getAttrGenericArgs()[0];

  909.                 typeClass = TypeManufacturerUtil.resolveGenericParameter(actualTypeArgument,
  910.                         manufacturingCtx, elementGenericTypeArgs);
  911.             }

  912.             fillCollection(manufacturingCtx,
  913.                     attributeMetadata.getAttributeAnnotations(), attributeName,
  914.                     retValue, typeClass, elementGenericTypeArgs.get());

  915.         } catch (SecurityException e) {
  916.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  917.                     e);
  918.         } catch (IllegalArgumentException e) {
  919.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  920.                     e);
  921.         } catch (InstantiationException e) {
  922.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  923.                     e);
  924.         } catch (IllegalAccessException e) {
  925.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  926.                     e);
  927.         } catch (ClassNotFoundException e) {
  928.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  929.                     e);
  930.         } catch (InvocationTargetException e) {
  931.             throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
  932.                     e);
  933.         }

  934.         return retValue;
  935.     }

  936.     /**
  937.      * Tries to find collection element type from collection object
  938.      *
  939.      * @param collection
  940.      *          The collection to be filled
  941.      * @param manufacturingCtx
  942.      *          the manufacturing context
  943.      * @param elementGenericTypeArgs
  944.      *          parameter to return generic arguments of collection element
  945.      * @param genericTypeArgs
  946.      *          The generic type arguments for the current generic class
  947.      *          instance
  948.      * @return
  949.      *        class type of collection element
  950.      */
  951.     private Class<?> findInheretedCollectionElementType(
  952.             Collection<Object> collection, ManufacturingContext manufacturingCtx,
  953.             AtomicReference<Type[]> elementGenericTypeArgs,
  954.             Type... genericTypeArgs) {

  955.         Class<?> pojoClass = collection.getClass();
  956.         Class<?> collectionClass = pojoClass;
  957.         Type[] typeParams = collectionClass.getTypeParameters();
  958.         main : while (typeParams.length < 1) {
  959.             for (Type genericIface : collectionClass.getGenericInterfaces()) {
  960.                 Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
  961.                         genericIface, manufacturingCtx, elementGenericTypeArgs);
  962.                 if (Collection.class.isAssignableFrom(clazz)) {
  963.                     collectionClass = clazz;
  964.                     typeParams = elementGenericTypeArgs.get();
  965.                     continue main;
  966.                 }
  967.             }
  968.             Type type = collectionClass.getGenericSuperclass();
  969.             if (type != null) {
  970.                 Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
  971.                         type, manufacturingCtx, elementGenericTypeArgs);
  972.                 if (Collection.class.isAssignableFrom(clazz)) {
  973.                     collectionClass = clazz;
  974.                     typeParams = elementGenericTypeArgs.get();
  975.                     continue main;
  976.                 }
  977.             }
  978.             if (Collection.class.equals(collectionClass)) {
  979.                 LOG.warn("Collection {} doesn't have generic types,"
  980.                         + "will use Object instead", pojoClass);
  981.                 typeParams = new Type[] { Object.class };
  982.             }
  983.         }
  984.         Class<?> elementTypeClass = TypeManufacturerUtil.resolveGenericParameter(typeParams[0],
  985.                     manufacturingCtx, elementGenericTypeArgs);
  986.         Type[] elementGenericArgs = ArrayUtils.addAll(
  987.                 elementGenericTypeArgs.get(), genericTypeArgs);
  988.         elementGenericTypeArgs.set(elementGenericArgs);
  989.         return elementTypeClass;
  990.     }

  991.     /**
  992.      * It fills a collection with the required number of elements of the
  993.      * required type.
  994.      *
  995.      * <p>
  996.      * This method has a so-called side effect. It updates the collection passed
  997.      * as argument.
  998.      * </p>
  999.      *
  1000.      * @param manufacturingCtx
  1001.      *            the manufacturing context
  1002.      * @param annotations
  1003.      *            The annotations for this attribute
  1004.      * @param attributeName
  1005.      *            The attribute name of collection in enclosing POJO class
  1006.      * @param collection
  1007.      *            The Collection to be filled
  1008.      * @param collectionElementType
  1009.      *            The type of the collection element
  1010.      * @param genericTypeArgs
  1011.      *            The generic type arguments for the current generic class
  1012.      *            instance
  1013.      * @throws InstantiationException
  1014.      *             If an exception occurred during instantiation
  1015.      * @throws IllegalAccessException
  1016.      *             If security was violated while creating the object
  1017.      * @throws InvocationTargetException
  1018.      *             If an exception occurred while invoking the constructor or
  1019.      *             factory method
  1020.      * @throws ClassNotFoundException
  1021.      *             If it was not possible to create a class from a string
  1022.      *
  1023.      */
  1024.     private void fillCollection(ManufacturingContext manufacturingCtx,
  1025.             List<Annotation> annotations, String attributeName,
  1026.             Collection<? super Object> collection,
  1027.             Class<?> collectionElementType, Type... genericTypeArgs)
  1028.             throws InstantiationException, IllegalAccessException,
  1029.             InvocationTargetException, ClassNotFoundException {

  1030.         // If the user defined a strategy to fill the collection elements,
  1031.         // we use it
  1032.         Holder<AttributeStrategy<?>> elementStrategyHolder
  1033.                 = new Holder<AttributeStrategy<?>>();
  1034.         Holder<AttributeStrategy<?>> keyStrategyHolder = null;
  1035.         Integer nbrElements = TypeManufacturerUtil.findCollectionSize(strategy, annotations,
  1036.                 collectionElementType, elementStrategyHolder, keyStrategyHolder);
  1037.         AttributeStrategy<?> elementStrategy = elementStrategyHolder.getValue();

  1038.         try {
  1039.             if (collection.size() > nbrElements) {

  1040.                 collection.clear();
  1041.             }

  1042.             for (int i = collection.size(); i < nbrElements; i++) {

  1043.                 // The default
  1044.                 Object element = TypeManufacturerUtil.returnAttributeDataStrategyValue(
  1045.                             collectionElementType, annotations, elementStrategy);

  1046.                 if (null == element) {

  1047.                     manufacturingCtx.backupTypeArgsMap(manufacturingCtx.createEmptyTypeArgsMap());
  1048.                     element = manufactureAttributeValue(collection, manufacturingCtx,
  1049.                             collectionElementType, collectionElementType,
  1050.                             annotations, attributeName, genericTypeArgs);
  1051.                     manufacturingCtx.restoreTypeArgsMap();
  1052.                 }

  1053.                 if (null != element) {
  1054.                     collection.add(element);
  1055.                 }
  1056.             }
  1057.         } catch (UnsupportedOperationException e) {

  1058.             LOG.warn("Cannot fill immutable collection {}", collection.getClass());
  1059.         }
  1060.     }

  1061.     /**
  1062.      * It manufactures and returns a Map with at least one element in it
  1063.      *
  1064.      * @param pojo
  1065.      *            The POJO being initialized
  1066.      * @param manufacturingCtx
  1067.      *            the manufacturing context
  1068.      * @param attributeMetadata
  1069.      *            The attribute's metadata
  1070.      * @return Map with at least one element in it
  1071.      *
  1072.      * @throws IllegalArgumentException
  1073.      *             <ul>
  1074.      *             <li>If the attribute name is null or empty</li>
  1075.      *             <li>If the array of types of the Map has length different
  1076.      *             from 2</li>
  1077.      *             </ul>
  1078.      *
  1079.      * @throws PodamMockeryException
  1080.      *             If an error occurred while creating the Map object
  1081.      */
  1082.     private Map<? super Object, ? super Object> resolveMapValueWhenMapIsPojoAttribute(
  1083.             Object pojo, ManufacturingContext manufacturingCtx,
  1084.             AttributeMetadata attributeMetadata) {

  1085.         String attributeName = attributeMetadata.getAttributeName();

  1086.         Map<Object, Object> defaultValue = null;
  1087.         if (null != pojo && !Character.isDigit(attributeName.charAt(0))) {

  1088.             defaultValue = PodamUtils.getFieldValue(pojo, attributeName);
  1089.         }

  1090.         Map<Object, Object> retValue;
  1091.         if (null != defaultValue &&
  1092.                 (defaultValue.getClass().getModifiers() & Modifier.PRIVATE) == 0) {
  1093.             /* Default map, which is not immutable */
  1094.             retValue = defaultValue;
  1095.         } else {

  1096.             @SuppressWarnings("unchecked")
  1097.             Class<Map<Object,Object>> mapType
  1098.                     = (Class<Map<Object, Object>>) attributeMetadata.getAttributeType();
  1099.             retValue = strategy.getTypeValue(attributeMetadata, manufacturingCtx, mapType);
  1100.             if (null != retValue && null != defaultValue) {
  1101.                 retValue.putAll(defaultValue);
  1102.             }
  1103.         }

  1104.         if (null == retValue) {
  1105.             return null;
  1106.         }

  1107.         try {

  1108.             Class<?> keyClass = null;

  1109.             Class<?> elementClass = null;

  1110.             AtomicReference<Type[]> keyGenericTypeArgs = new AtomicReference<Type[]>(
  1111.                     PodamConstants.NO_TYPES);
  1112.             AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
  1113.                     PodamConstants.NO_TYPES);
  1114.             if (ArrayUtils.isEmpty(attributeMetadata.getAttrGenericArgs())) {

  1115.                 MapArguments mapArgs = findInheretedMapElementType(retValue,
  1116.                         manufacturingCtx,
  1117.                         attributeMetadata.getAttrGenericArgs());

  1118.                 keyClass = mapArgs.getKeyOrValueType();

  1119.                 elementClass = mapArgs.getElementClass();

  1120.             } else {

  1121.                 // Expected only key, value type
  1122.                 if (attributeMetadata.getAttrGenericArgs().length != 2) {
  1123.                     throw new IllegalStateException(
  1124.                             "In a Map only key value generic type are expected,"
  1125.                             + "but received " + Arrays.toString(attributeMetadata.getAttrGenericArgs()));
  1126.                 }

  1127.                 Type[] actualTypeArguments = attributeMetadata.getAttrGenericArgs();
  1128.                 keyClass = TypeManufacturerUtil.resolveGenericParameter(actualTypeArguments[0],
  1129.                         manufacturingCtx, keyGenericTypeArgs);
  1130.                 elementClass = TypeManufacturerUtil.resolveGenericParameter(actualTypeArguments[1],
  1131.                         manufacturingCtx, elementGenericTypeArgs);
  1132.             }

  1133.             MapArguments mapArguments = new MapArguments();
  1134.             mapArguments.setAttributeName(attributeName);
  1135.             mapArguments.getAnnotations().addAll(attributeMetadata.getAttributeAnnotations());
  1136.             mapArguments.setMapToBeFilled(retValue);
  1137.             mapArguments.setKeyOrValueType(keyClass);
  1138.             mapArguments.setElementClass(elementClass);
  1139.             mapArguments.setKeyGenericTypeArgs(keyGenericTypeArgs.get());
  1140.             mapArguments
  1141.                     .setElementGenericTypeArgs(elementGenericTypeArgs.get());

  1142.             fillMap(mapArguments, manufacturingCtx);

  1143.         } catch (InstantiationException e) {
  1144.             throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
  1145.         } catch (IllegalAccessException e) {
  1146.             throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
  1147.         } catch (SecurityException e) {
  1148.             throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
  1149.         } catch (ClassNotFoundException e) {
  1150.             throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
  1151.         } catch (InvocationTargetException e) {
  1152.             throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
  1153.         }

  1154.         return retValue;
  1155.     }

  1156.     /**
  1157.      * Finds key and element type arguments
  1158.      *
  1159.      * @param map
  1160.      *          The map being initialized
  1161.      * @param manufacturingCtx
  1162.      *          the manufacturing context
  1163.      * @param genericTypeArgs
  1164.      *          The generic type arguments for the current generic class
  1165.      *          instance
  1166.      * @return
  1167.      *        Inherited map key and element types
  1168.      *
  1169.      */
  1170.     private MapArguments findInheretedMapElementType(Map<Object, Object> map,
  1171.             ManufacturingContext manufacturingCtx,
  1172.             Type... genericTypeArgs) {

  1173.         Class<?> pojoClass = map.getClass();
  1174.         Class<?> mapClass = pojoClass;
  1175.         AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
  1176.                 PodamConstants.NO_TYPES);
  1177.         Type[] typeParams = mapClass.getTypeParameters();
  1178.         main : while (typeParams.length < 2) {
  1179.             for (Type genericIface : mapClass.getGenericInterfaces()) {
  1180.                 Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
  1181.                         genericIface, manufacturingCtx, elementGenericTypeArgs);
  1182.                 if (Map.class.isAssignableFrom(clazz)) {
  1183.                     typeParams = elementGenericTypeArgs.get();
  1184.                     mapClass = clazz;
  1185.                     continue main;
  1186.                 }
  1187.             }
  1188.             Type type = mapClass.getGenericSuperclass();
  1189.             if (type != null) {
  1190.                 Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
  1191.                         type, manufacturingCtx, elementGenericTypeArgs);
  1192.                 if (Map.class.isAssignableFrom(clazz)) {
  1193.                     typeParams = elementGenericTypeArgs.get();
  1194.                     mapClass = clazz;
  1195.                     continue main;
  1196.                 }
  1197.             }
  1198.             if (Map.class.equals(mapClass)) {
  1199.                 LOG.warn("Map {} doesn't have generic types,"
  1200.                         + "will use Object, Object instead", pojoClass);
  1201.                 typeParams = new Type[] { Object.class, Object.class };
  1202.             }
  1203.         }
  1204.         AtomicReference<Type[]> keyGenericTypeArgs = new AtomicReference<Type[]>(
  1205.                 PodamConstants.NO_TYPES);
  1206.         Class<?> keyClass = TypeManufacturerUtil.resolveGenericParameter(typeParams[0],
  1207.                     manufacturingCtx, keyGenericTypeArgs);
  1208.         Class<?> elementClass = TypeManufacturerUtil.resolveGenericParameter(
  1209.                 typeParams[1], manufacturingCtx, elementGenericTypeArgs);

  1210.         Type[] keyGenericArgs = ArrayUtils.addAll(keyGenericTypeArgs.get(),
  1211.                 genericTypeArgs);
  1212.         Type[] elementGenericArgs = ArrayUtils.addAll(elementGenericTypeArgs.get(),
  1213.                 genericTypeArgs);

  1214.         MapArguments mapArguments = new MapArguments();
  1215.         for (Annotation annotation : pojoClass.getAnnotations()) {
  1216.             mapArguments.getAnnotations().add(annotation);
  1217.         }
  1218.         mapArguments.setMapToBeFilled(map);
  1219.         mapArguments.setKeyOrValueType(keyClass);
  1220.         mapArguments.setElementClass(elementClass);
  1221.         mapArguments.setKeyGenericTypeArgs(keyGenericArgs);
  1222.         mapArguments.setElementGenericTypeArgs(elementGenericArgs);

  1223.         return mapArguments;
  1224.     }

  1225.     /**
  1226.      * It fills a Map with the required number of elements of the required type.
  1227.      *
  1228.      * <p>
  1229.      * This method has a so-called side-effect. It updates the Map given as
  1230.      * argument.
  1231.      * </p>
  1232.      *
  1233.      * @param mapArguments
  1234.      *             The arguments POJO
  1235.      * @param manufacturingCtx
  1236.      *             Manufacturing context
  1237.      * @throws InstantiationException
  1238.      *             If an exception occurred during instantiation
  1239.      * @throws IllegalAccessException
  1240.      *             If security was violated while creating the object
  1241.      * @throws InvocationTargetException
  1242.      *             If an exception occurred while invoking the constructor or
  1243.      *             factory method
  1244.      * @throws ClassNotFoundException
  1245.      *             If it was not possible to create a class from a string
  1246.      *
  1247.      */
  1248.     private void fillMap(MapArguments mapArguments, ManufacturingContext manufacturingCtx)
  1249.             throws InstantiationException, IllegalAccessException,
  1250.             InvocationTargetException, ClassNotFoundException {

  1251.         // If the user defined a strategy to fill the collection elements,
  1252.         // we use it
  1253.         Holder<AttributeStrategy<?>> elementStrategyHolder
  1254.                 = new Holder<AttributeStrategy<?>>();
  1255.         Holder<AttributeStrategy<?>> keyStrategyHolder
  1256.                 = new Holder<AttributeStrategy<?>>();
  1257.         Integer nbrElements = TypeManufacturerUtil.findCollectionSize(strategy, mapArguments.getAnnotations(),
  1258.                 mapArguments.getElementClass(), elementStrategyHolder,
  1259.                 keyStrategyHolder);
  1260.         AttributeStrategy<?> keyStrategy = keyStrategyHolder.getValue();
  1261.         AttributeStrategy<?> elementStrategy = elementStrategyHolder.getValue();

  1262.         Map<? super Object, ? super Object> map = mapArguments.getMapToBeFilled();
  1263.         try {
  1264.             if (map.size() > nbrElements) {

  1265.                 map.clear();
  1266.             }

  1267.             for (int i = map.size(); i < nbrElements; i++) {

  1268.                 Object keyValue = null;

  1269.                 Object elementValue = null;

  1270.                 MapKeyOrElementsArguments valueArguments = new MapKeyOrElementsArguments();
  1271.                 valueArguments.setAttributeName(mapArguments.getAttributeName());
  1272.                 valueArguments.setMapToBeFilled(mapArguments.getMapToBeFilled());
  1273.                 valueArguments.getAnnotations().addAll(mapArguments.getAnnotations());
  1274.                 valueArguments.setKeyOrValueType(mapArguments.getKeyOrValueType());
  1275.                 valueArguments.setElementStrategy(keyStrategy);
  1276.                 valueArguments.setGenericTypeArgs(mapArguments
  1277.                         .getKeyGenericTypeArgs());

  1278.                 keyValue = getMapKeyOrElementValue(valueArguments, manufacturingCtx);

  1279.                 valueArguments.setKeyOrValueType(mapArguments.getElementClass());
  1280.                 valueArguments.setElementStrategy(elementStrategy);
  1281.                 valueArguments.setGenericTypeArgs(mapArguments
  1282.                         .getElementGenericTypeArgs());

  1283.                 elementValue = getMapKeyOrElementValue(valueArguments, manufacturingCtx);

  1284.                 if (elementValue != null) {
  1285.                     map.put(keyValue, elementValue);
  1286.                 }
  1287.             }
  1288.         } catch (UnsupportedOperationException e) {

  1289.             LOG.warn("Cannot fill immutable map {}", map.getClass());
  1290.         }
  1291.     }

  1292.     /**
  1293.      * It fills a Map key or value with the appropriate value, considering
  1294.      * attribute-level customisation.
  1295.      *
  1296.      * @param keyOrElementsArguments
  1297.      *            The arguments POJO
  1298.      * @param manufacturingCtx
  1299.      *             manufacturing context
  1300.      * @return A Map key or value
  1301.      * @throws InstantiationException
  1302.      *             If an exception occurred during instantiation
  1303.      * @throws IllegalAccessException
  1304.      *             If security was violated while creating the object
  1305.      * @throws InvocationTargetException
  1306.      *             If an exception occurred while invoking the constructor or
  1307.      *             factory method
  1308.      * @throws IllegalArgumentException
  1309.      *             <ul>
  1310.      *             <li>If an illegal argument was passed</li>
  1311.      *             <li>If an invalid value was set for a precise value in an
  1312.      *             annotation and such value could not be converted to the
  1313.      *             desired type</li>
  1314.      *             </ul>
  1315.      * @throws ClassNotFoundException
  1316.      *             If manufactured class could not be loaded
  1317.      */
  1318.     private Object getMapKeyOrElementValue(
  1319.             MapKeyOrElementsArguments keyOrElementsArguments,
  1320.             ManufacturingContext manufacturingCtx)
  1321.             throws InstantiationException, IllegalAccessException,
  1322.             InvocationTargetException, ClassNotFoundException {

  1323.         AttributeStrategy<?> strategy = keyOrElementsArguments.getElementStrategy();
  1324.         Object retValue = TypeManufacturerUtil.returnAttributeDataStrategyValue(
  1325.                     keyOrElementsArguments.getKeyOrValueType(),
  1326.                     keyOrElementsArguments.getAnnotations(),
  1327.                     strategy);

  1328.         if (null == retValue) {

  1329.             manufacturingCtx.backupTypeArgsMap(manufacturingCtx.createEmptyTypeArgsMap());
  1330.             retValue = manufactureAttributeValue(
  1331.                     keyOrElementsArguments.getMapToBeFilled(),
  1332.                     manufacturingCtx,
  1333.                     keyOrElementsArguments.getKeyOrValueType(),
  1334.                     keyOrElementsArguments.getKeyOrValueType(),
  1335.                     keyOrElementsArguments.getAnnotations(),
  1336.                     keyOrElementsArguments.getAttributeName(),
  1337.                     keyOrElementsArguments.getGenericTypeArgs());
  1338.             manufacturingCtx.restoreTypeArgsMap();
  1339.         }
  1340.         return retValue;
  1341.     }

  1342.     /**
  1343.      * It fills an Array with the required number of elements of the required type.
  1344.      *
  1345.      * <p>
  1346.      * This method has a so-called side-effect. It updates the Map given as
  1347.      * argument.
  1348.      * </p>
  1349.      *
  1350.      * @param array
  1351.      *             The array POJO
  1352.      * @param attributeName
  1353.      *            The attribute name of collection in enclosing POJO class
  1354.      * @param elementType
  1355.      *            The generic type of the collection element
  1356.      * @param genericElementType
  1357.      *            The generic type of the collection element
  1358.      * @param annotations
  1359.      *            The annotations for this attribute
  1360.      * @param manufacturingCtx
  1361.      *             Manufacturing context
  1362.      * @throws InstantiationException
  1363.      *             If an exception occurred during instantiation
  1364.      * @throws IllegalAccessException
  1365.      *             If security was violated while creating the object
  1366.      * @throws InvocationTargetException
  1367.      *             If an exception occurred while invoking the constructor or
  1368.      *             factory method
  1369.      * @throws ClassNotFoundException
  1370.      *             If it was not possible to create a class from a string
  1371.      *
  1372.      */
  1373.     private void fillArray(Object array, String attributeName, Class<?> elementType,
  1374.             Type genericElementType, List<Annotation> annotations,
  1375.             ManufacturingContext manufacturingCtx)
  1376.             throws InstantiationException, IllegalAccessException,
  1377.             InvocationTargetException, ClassNotFoundException {

  1378.         Class<?> componentType = array.getClass().getComponentType();
  1379.         Type genericComponentType;
  1380.         AtomicReference<Type[]> genericTypeArgs = new AtomicReference<Type[]>(
  1381.                 PodamConstants.NO_TYPES);
  1382.         if (genericElementType instanceof GenericArrayType) {
  1383.             genericComponentType = ((GenericArrayType) genericElementType).getGenericComponentType();
  1384.             if (genericComponentType instanceof TypeVariable) {
  1385.                 TypeVariable<?> componentTypeVariable
  1386.                          = (TypeVariable<?>) genericComponentType;
  1387.                 final Type resolvedType
  1388.                          = manufacturingCtx.resolveType(componentTypeVariable.getName());
  1389.                 componentType
  1390.                          = TypeManufacturerUtil.resolveGenericParameter(resolvedType,
  1391.                                 manufacturingCtx, genericTypeArgs);
  1392.             }
  1393.         } else {
  1394.             genericComponentType = componentType;
  1395.         }

  1396.         // If the user defined a strategy to fill the collection elements,
  1397.         // we use it
  1398.         Holder<AttributeStrategy<?>> elementStrategyHolder
  1399.                 = new Holder<AttributeStrategy<?>>();
  1400.         Holder<AttributeStrategy<?>> keyStrategyHolder = null;
  1401.         TypeManufacturerUtil.findCollectionSize(strategy,
  1402.                 annotations, elementType,
  1403.                 elementStrategyHolder, keyStrategyHolder);
  1404.         AttributeStrategy<?> elementStrategy = elementStrategyHolder.getValue();

  1405.         int nbrElements = Array.getLength(array);
  1406.         for (int i = 0; i < nbrElements; i++) {

  1407.             Object arrayElement = Array.get(array, i);

  1408.             if (null == arrayElement || arrayElement.getClass().isPrimitive() || arrayElement instanceof Number) {
  1409.                 // The default
  1410.                 arrayElement = TypeManufacturerUtil.returnAttributeDataStrategyValue(
  1411.                         componentType, annotations, elementStrategy);

  1412.                 if (null == arrayElement) {
  1413.                     arrayElement = manufactureAttributeValue(array, manufacturingCtx,
  1414.                             componentType, genericComponentType,
  1415.                             annotations, attributeName,
  1416.                             genericTypeArgs.get());
  1417.                 }

  1418.                 Array.set(array, i, arrayElement);
  1419.             }
  1420.         }
  1421.     }

  1422.     /**
  1423.      * It returns an Array with the first element set
  1424.      *
  1425.      *
  1426.      * @param pojo
  1427.      *            POJO containing attribute
  1428.      * @param manufacturingCtx
  1429.      *          the manufacturing context
  1430.      * @param attributeMetadata
  1431.      *            The attribute's metadata
  1432.      * @return Array with the first element set
  1433.      * @throws IllegalArgumentException
  1434.      *             If an illegal argument was passed to the constructor
  1435.      * @throws InstantiationException
  1436.      *             If an exception occurred during instantiation
  1437.      * @throws IllegalAccessException
  1438.      *             If security was violated while creating the object
  1439.      * @throws InvocationTargetException
  1440.      *             If an exception occurred while invoking the constructor or
  1441.      *             factory method
  1442.      * @throws ClassNotFoundException
  1443.      *             If it was not possible to create a class from a string
  1444.      */
  1445.     private Object resolveArrayElementValue(Object pojo,
  1446.             ManufacturingContext manufacturingCtx,
  1447.             AttributeMetadata attributeMetadata) throws InstantiationException,
  1448.             IllegalAccessException, InvocationTargetException,
  1449.             ClassNotFoundException {

  1450.         @SuppressWarnings("unchecked")
  1451.         Class<Object> arrayType
  1452.                 = (Class<Object>) attributeMetadata.getAttributeType();
  1453.         Object array = strategy.getTypeValue(attributeMetadata, manufacturingCtx, arrayType);
  1454.         fillArray(array, attributeMetadata.getAttributeName(),
  1455.                 attributeMetadata.getAttributeType(),
  1456.                 attributeMetadata.getAttributeGenericType(),
  1457.                 attributeMetadata.getAttributeAnnotations(),
  1458.                 manufacturingCtx);
  1459.         return array;
  1460.     }


  1461.     /**
  1462.      * Given a constructor it manufactures and returns the parameter values
  1463.      * required to invoke it
  1464.      *
  1465.      * @param constructor
  1466.      *            The constructor for which parameter values are required
  1467.      * @param pojoClass
  1468.      *            The POJO class containing the constructor
  1469.      * @param manufacturingCtx
  1470.      *          the manufacturing context
  1471.      * @param genericTypeArgs
  1472.      *            The generic type arguments for the current generic class
  1473.      *            instance
  1474.      *
  1475.      * @return The parameter values required to invoke the constructor
  1476.      * @throws IllegalArgumentException
  1477.      *             If an illegal argument was passed to the constructor
  1478.      * @throws InstantiationException
  1479.      *             If an exception occurred during instantiation
  1480.      * @throws IllegalAccessException
  1481.      *             If security was violated while creating the object
  1482.      * @throws InvocationTargetException
  1483.      *             If an exception occurred while invoking the constructor or
  1484.      *             factory method
  1485.      * @throws ClassNotFoundException
  1486.      *             If it was not possible to create a class from a string
  1487.      */
  1488.     private Object[] getParameterValuesForConstructor(
  1489.             Constructor<?> constructor, Class<?> pojoClass,
  1490.             ManufacturingContext manufacturingCtx,
  1491.             Type... genericTypeArgs)
  1492.             throws InstantiationException, IllegalAccessException,
  1493.             InvocationTargetException, ClassNotFoundException {

  1494.         Class<?>[] parameterTypes = constructor.getParameterTypes();

  1495.         if (parameterTypes.length == 0) {

  1496.             return PodamConstants.NO_ARGS;

  1497.         } else {

  1498.             Object[] parameterValues = new Object[parameterTypes.length];

  1499.             Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
  1500.             Type[] genericTypes = constructor.getGenericParameterTypes();
  1501.             String ctorName = Arrays.toString(genericTypes);

  1502.             for (int idx = 0; idx < parameterTypes.length; idx++) {

  1503.                 List<Annotation> annotations = Arrays
  1504.                         .asList(parameterAnnotations[idx]);

  1505.                 Type genericType = (idx < genericTypes.length) ?
  1506.                         genericTypes[idx] : parameterTypes[idx];

  1507.                 parameterValues[idx] = manufactureParameterValue(pojoClass,
  1508.                         idx + ctorName, parameterTypes[idx], genericType,
  1509.                         annotations, manufacturingCtx, genericTypeArgs);
  1510.             }
  1511.             return parameterValues;
  1512.         }
  1513.     }

  1514.     /**
  1515.      * Given a method it manufactures and returns the parameter values
  1516.      * required to invoke it
  1517.      *
  1518.      * @param method
  1519.      *            The method for which parameter values are required
  1520.      * @param pojoClass
  1521.      *            The POJO class containing the constructor
  1522.      * @param manufacturingCtx
  1523.      *          the manufacturing context
  1524.      * @param genericTypeArgs
  1525.      *            The generic type arguments for the current generic class
  1526.      *            instance
  1527.      *
  1528.      * @return The parameter values required to invoke the method
  1529.      * @throws IllegalArgumentException
  1530.      *             If an illegal argument was passed to the method
  1531.      * @throws InstantiationException
  1532.      *             If an exception occurred during instantiation
  1533.      * @throws IllegalAccessException
  1534.      *             If security was violated while creating the object
  1535.      * @throws InvocationTargetException
  1536.      *             If an exception occurred while invoking the constructor or
  1537.      *             factory method
  1538.      * @throws ClassNotFoundException
  1539.      *             If it was not possible to create a class from a string
  1540.      */
  1541.     private Object[] getParameterValuesForMethod(
  1542.             Method method, Class<?> pojoClass,
  1543.             ManufacturingContext manufacturingCtx,
  1544.             Type... genericTypeArgs)
  1545.             throws InstantiationException, IllegalAccessException,
  1546.             InvocationTargetException, ClassNotFoundException {

  1547.         Class<?>[] parameterTypes = method.getParameterTypes();

  1548.         if (parameterTypes.length == 0) {

  1549.             return PodamConstants.NO_ARGS;

  1550.         } else {

  1551.             Object[] parameterValues = new Object[parameterTypes.length];

  1552.             Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  1553.             Type[] genericTypes = method.getGenericParameterTypes();
  1554.             String methodName = Arrays.toString(genericTypes);

  1555.             for (int idx = 0; idx < parameterTypes.length; idx++) {

  1556.                 List<Annotation> annotations = Arrays
  1557.                         .asList(parameterAnnotations[idx]);

  1558.                 Type genericType = (idx < genericTypes.length) ?
  1559.                         genericTypes[idx] : parameterTypes[idx];

  1560.                 parameterValues[idx] = manufactureParameterValue(pojoClass,
  1561.                         idx + methodName, parameterTypes[idx], genericType,
  1562.                         annotations, manufacturingCtx, genericTypeArgs);
  1563.             }
  1564.             return parameterValues;
  1565.         }
  1566.     }

  1567.     /**
  1568.      * Manufactures and returns the parameter value for method required to
  1569.      * invoke it
  1570.      *
  1571.      * @param pojoClass pojo class
  1572.      * @param parameterName name of parameter
  1573.      * @param parameterType type of parameter
  1574.      * @param genericType generic type of parameter
  1575.      * @param annotations parameter annotations
  1576.      * @param manufacturingCtx
  1577.      *            the manufacturing context
  1578.      * @param genericTypeArgs
  1579.      *            The generic type arguments for the current generic class
  1580.      *            instance
  1581.      *
  1582.      * @return The parameter values required to invoke the constructor
  1583.      * @throws IllegalArgumentException
  1584.      *             If an illegal argument was passed to the constructor
  1585.      * @throws InstantiationException
  1586.      *             If an exception occurred during instantiation
  1587.      * @throws IllegalAccessException
  1588.      *             If security was violated while creating the object
  1589.      * @throws InvocationTargetException
  1590.      *             If an exception occurred while invoking the constructor or
  1591.      *             factory method
  1592.      * @throws ClassNotFoundException
  1593.      *             If it was not possible to create a class from a string
  1594.      */
  1595.     private Object manufactureParameterValue(Class<?> pojoClass,
  1596.             String parameterName, Class<?> parameterType, Type genericType,
  1597.             final List<Annotation> annotations,
  1598.             ManufacturingContext manufacturingCtx,
  1599.             Type... genericTypeArgs)
  1600.             throws InstantiationException, IllegalAccessException,
  1601.             InvocationTargetException, ClassNotFoundException {

  1602.         AttributeStrategy<?> attributeStrategy
  1603.                 = TypeManufacturerUtil.findAttributeStrategy(strategy, annotations, parameterType);
  1604.         if (null != attributeStrategy) {

  1605.             return TypeManufacturerUtil.returnAttributeDataStrategyValue(
  1606.                     parameterType, annotations, attributeStrategy);
  1607.         }

  1608.         final boolean cloneTypeArgsMap = (genericType instanceof ParameterizedType);
  1609.         Type[] genericTypeArgsAll;
  1610.         if (cloneTypeArgsMap) {
  1611.             genericTypeArgsAll = manufacturingCtx.cloneTypeArgsMap(
  1612.                     parameterType, (ParameterizedType) genericType, genericTypeArgs);
  1613.         } else {
  1614.             genericTypeArgsAll = genericTypeArgs;
  1615.         }

  1616.         Object retValue = manufactureAttributeValue(pojoClass, manufacturingCtx, parameterType,
  1617.                 genericType, annotations, parameterName,
  1618.                 genericTypeArgsAll);
  1619.         if (cloneTypeArgsMap) {
  1620.             manufacturingCtx.restoreTypeArgsMap();
  1621.         }
  1622.         return retValue;
  1623.     }

  1624.     /**
  1625.      * Returns a value for an abstract type or interface if possible.
  1626.      * @param pojoClass The Pojo class
  1627.      * @param pojoMetadata The Pojo metadata
  1628.      * @param manufacturingCtx The manufacturing context
  1629.      * @param genericTypeArgs The generic type arguments map
  1630.      * @param <T> The type of the value to be returned
  1631.      * @return a value or null, if manufacturing didn't succeed
  1632.      * @throws InstantiationException If a problem occurred while instantiating the object
  1633.      * @throws IllegalAccessException If a problem occurred while instantiating the object
  1634.      * @throws InvocationTargetException If a problem occurred while instantiating the object
  1635.      * @throws ClassNotFoundException If a problem occurred while instantiating the object
  1636.      */
  1637.     private <T> T getValueForAbstractType(Class<T> pojoClass,
  1638.                                           AttributeMetadata pojoMetadata,
  1639.                                           ManufacturingContext manufacturingCtx,
  1640.                                           Type[] genericTypeArgs)
  1641.             throws InstantiationException, IllegalAccessException,
  1642.             InvocationTargetException, ClassNotFoundException {

  1643.         Class<? extends T> specificClass = strategy.getSpecificClass(pojoClass);

  1644.         if (!specificClass.equals(pojoClass)) {

  1645.             return this.manufacturePojoInternal(specificClass, pojoMetadata,
  1646.                     manufacturingCtx, genericTypeArgs);
  1647.         }

  1648.         Class<?> factory = strategy.getFactoryClass(pojoClass);
  1649.         if (factory != null) {
  1650.             T retValue = instantiatePojoWithFactory(factory, pojoClass,
  1651.                 manufacturingCtx, genericTypeArgs);
  1652.             if (retValue != null) {
  1653.                 return retValue;
  1654.             }
  1655.         }

  1656.         return resortToExternalFactory(manufacturingCtx,
  1657.                 "Cannot instantiate a class {}. Resorting to {} external factory",
  1658.                 pojoClass, genericTypeArgs);
  1659.     }


  1660. }