PodamFactoryImpl.java

/**
 *
 */
package uk.co.jemos.podam.api;

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

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.co.jemos.podam.api.DataProviderStrategy.Order;
import uk.co.jemos.podam.common.AttributeStrategy;
import uk.co.jemos.podam.common.Holder;
import uk.co.jemos.podam.common.ManufacturingContext;
import uk.co.jemos.podam.common.PodamConstants;
import uk.co.jemos.podam.common.PodamConstructor;
import uk.co.jemos.podam.exceptions.PodamMockeryException;
import uk.co.jemos.podam.typeManufacturers.TypeManufacturerUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;


/**
 * The PODAM factory implementation
 *
 * @author mtedone
 *
 * @since 1.0.0
 *
 */
@NotThreadSafe
@Immutable
public class PodamFactoryImpl implements PodamFactory {

    // ------------------->> Constants

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

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

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

	// ------------------->> Instance / variables

	/**
	 * External factory to delegate production this factory cannot handle
	 * <p>
	 * The default is {@link NullExternalFactory}.
	 * </p>
	 */
	private PodamFactory externalFactory
			= NullExternalFactory.getInstance();

	/**
	 * The strategy to use to fill data.
	 * <p>
	 * The default is {@link RandomDataProviderStrategyImpl}.
	 * </p>
	 */
	private DataProviderStrategy strategy
			= new RandomDataProviderStrategyImpl();

	/**
	 * The strategy to use to introspect data.
	 * <p>
	 * The default is {@link DefaultClassInfoStrategy}.
	 * </p>
	 */
	private ClassInfoStrategy classInfoStrategy
			= DefaultClassInfoStrategy.getInstance();

	// ------------------->> Constructors

	/**
	 * Default constructor.
	 */
	public PodamFactoryImpl() {
		this(NullExternalFactory.getInstance(),
				new RandomDataProviderStrategyImpl());
	}

	/**
	 * Constructor with non-default strategy
	 *
	 * @param strategy
	 *            The strategy to use to fill data
	 */
	public PodamFactoryImpl(DataProviderStrategy strategy) {

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

	/**
	 * Constructor with non-default external factory
	 *
	 * @param externalFactory
	 *            External factory to delegate production this factory cannot
	 *            handle
	 */
	public PodamFactoryImpl(PodamFactory externalFactory) {
		this(externalFactory, new RandomDataProviderStrategyImpl());
	}

	/**
	 * Full constructor.
	 *
	 * @param externalFactory
	 *            External factory to delegate production this factory cannot
	 *            handle
	 * @param strategy
	 *            The strategy to use to fill data
	 */
	public PodamFactoryImpl(PodamFactory externalFactory,
			DataProviderStrategy strategy) {
		this.externalFactory = externalFactory;
		this.strategy = strategy;
	}

	// ------------------->> Public methods

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T manufacturePojoWithFullData(Class<T> pojoClass, Type... genericTypeArgs) {
		ManufacturingContext manufacturingCtx = new ManufacturingContext();
		manufacturingCtx.getPojos().put(pojoClass, 1);
		manufacturingCtx.setConstructorOrdering(Order.HEAVY_FIRST);
		return doManufacturePojo(pojoClass, manufacturingCtx, genericTypeArgs);
    }

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T manufacturePojo(Class<T> pojoClass, Type... genericTypeArgs) {
		ManufacturingContext manufacturingCtx = new ManufacturingContext();
		manufacturingCtx.getPojos().put(pojoClass, 1);
		return doManufacturePojo(pojoClass, manufacturingCtx, genericTypeArgs);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T populatePojo(T pojo, Type... genericTypeArgs) {
		ManufacturingContext manufacturingCtx = new ManufacturingContext();
		manufacturingCtx.getPojos().put(pojo.getClass(), 1);
		Type[] genericTypeArgsExtra = ManufacturingContext.fillTypeArgsMap(
				manufacturingCtx, pojo.getClass(), genericTypeArgs);
		try {
			List<Annotation> annotations = null;
			return this.populatePojoInternal(pojo, annotations,
					manufacturingCtx, genericTypeArgsExtra);
		} catch (InstantiationException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (ClassNotFoundException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		}
	}

	// ------------------->> Getters / Setters

	/**
	 * {@inheritDoc}
	 */
	@Override
	public DataProviderStrategy getStrategy() {
		return strategy;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PodamFactory setStrategy(DataProviderStrategy strategy) {
		this.strategy = strategy;
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ClassInfoStrategy getClassStrategy() {
		return classInfoStrategy;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PodamFactory setClassStrategy(ClassInfoStrategy classInfoStrategy) {
		this.classInfoStrategy = classInfoStrategy;
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PodamFactory getExternalFactory() {
		return externalFactory;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PodamFactory setExternalFactory(PodamFactory externalFactory) {
		this.externalFactory = externalFactory;
		return this;
	}

	// ------------------->> Private methods

	/**
	 * It attempts to create an instance of the given class with a static method
	 * of the factory
	 * <p>
	 * This method attempts to instantiate POJO with a static method of provided
	 * factory, for example, getInstance().
	 * </p>
	 *
	 * @param <T>
	 *            The type of Pojo class
	 * @param factoryClass
	 *            The factory class, which will be used for POJO instantiation
	 * @param pojoClass
	 *            The name of the class for which an instance filled with values
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 *
	 *
	 * @return An instance of the given class
	 * @throws IllegalArgumentException
	 *             If an illegal argument was passed to the constructor
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 */
	private <T> T instantiatePojoWithFactory(
			Class<?> factoryClass, Class<T> pojoClass,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

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

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

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

		for (Method candidateConstructor : declaredMethods) {

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

			parameterValues = getParameterValuesForMethod(candidateConstructor,
					pojoClass, manufacturingCtx, genericTypeArgs);

			try {

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

			} catch (Exception t) {

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

			}

		}

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

	}

    /**
	 * It creates and returns an instance of the given class if at least one of
	 * its constructors has been annotated with {@link PodamConstructor}
	 *
	 * @param <T>
	 *            The type of the instance to return
	 *
	 * @param pojoClass
	 *            The class of which an instance is required
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return an instance of the given class if at least one of its
	 *         constructors has been annotated with {@link PodamConstructor}
	 * @throws SecurityException
	 *             If an security was violated
	 */
	private <T> T instantiatePojo(Class<T> pojoClass,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws SecurityException {

		T retValue = null;

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

			/* Then non-public constructors */
			if (retValue == null) {
				constructors = pojoClass.getDeclaredConstructors();
			}
		}

		if (retValue == null) {

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

			for (Constructor<?> constructor : constructors) {

				try {
					Object[] parameterValues = getParameterValuesForConstructor(
							constructor, pojoClass, manufacturingCtx,
							genericTypeArgs);

					// Security hack
					if (!constructor.isAccessible()) {
						constructor.setAccessible(true);
					}

					@SuppressWarnings("unchecked")
					T tmp = (T) constructor.newInstance(parameterValues);
					retValue = tmp;
					LOG.debug("We could create an instance with constructor: "
							+ constructor);
					break;
				} catch (Exception e) {
					LOG.debug("We couldn't create an instance for pojo: {} with"
							+ " constructor: {}. Will try with another one.",
							pojoClass, constructor, e);
				}
			}
		}

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

	/**
	 * Manufactures and populates the pojo class
	 *
	 * @param <T> The type of the instance to return
	 * @param pojoClass the class to instantiate
	 * @param manufacturingCtx the initialized manufacturing context
	 * @param genericTypeArgs generic arguments for the pojo class
	 * @return instance of @pojoClass or null in case it cannot be instantiated
	 */
	private <T> T doManufacturePojo(Class<T> pojoClass,
			ManufacturingContext manufacturingCtx, Type... genericTypeArgs) {
		try {
			Class<?> declaringClass = null;
			Object declaringInstance = null;
			AttributeMetadata pojoMetadata = new AttributeMetadata(pojoClass,
					pojoClass, genericTypeArgs, declaringClass, declaringInstance);
			return this.manufacturePojoInternal(pojoClass, pojoMetadata,
                    manufacturingCtx, genericTypeArgs);
		} catch (InstantiationException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		} catch (ClassNotFoundException e) {
			throw new PodamMockeryException(e.getMessage(), e);
		}
	}

	/**
	 * Generic method which returns an instance of the given class filled with
	 * values dictated by the strategy
	 *
	 * @param <T>
	 *            The type for which a filled instance is required
	 *
	 * @param pojoClass
	 *            The name of the class for which an instance filled with values
	 *            is required
	 * @param pojoMetadata
	 *            attribute metadata for POJOs produced recursively
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return An instance of &lt;T&gt; filled with dummy values
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If manufactured class cannot be loaded
	 * @throws PodamMockeryException
	 *             if a problem occurred while creating a POJO instance or while
	 *             setting its state
	 */
	private <T> T manufacturePojoInternal(Class<T> pojoClass,
			AttributeMetadata pojoMetadata, ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

		// reuse object from memoization table
		@SuppressWarnings("unchecked")
		T objectToReuse = (T) strategy.getMemoizedObject(pojoMetadata);
		if (objectToReuse != null) {
			LOG.debug("Fetched memoized object for {} with parameters {}",
					pojoClass, Arrays.toString(genericTypeArgs));
			return objectToReuse;
		} else {
			LOG.debug("Manufacturing {} with parameters {}",
					pojoClass, Arrays.toString(genericTypeArgs));
		}

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

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

			try {

				retValue = instantiatePojo(pojoClass, manufacturingCtx,
						genericTypeArgsExtra);
			} catch (SecurityException e) {

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

		if (retValue == null) {
			retValue = getValueForAbstractType(pojoClass, pojoMetadata,
					manufacturingCtx, genericTypeArgs);
		} else {

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

			List<Annotation> annotations = null;
			populatePojoInternal(retValue, annotations, manufacturingCtx,
					genericTypeArgsExtra);
		}

		manufacturingCtx.restoreTypeArgsMap();
		return retValue;
	}



    /**
	 * Fills given class filled with values dictated by the strategy
	 *
	 * @param <T>
	 *            The type for which should be populated
	 * @param pojo
	 *            An instance to be filled with dummy values
	 * @param annotations
	 *            a list of annotations attached to this POJO defined elsewhere 
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return An instance of &lt;T&gt; filled with dummy values
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If manufactured class cannot be loaded
	 */
	private <T> T populatePojoInternal(T pojo, List<Annotation> annotations,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

		Class<?> pojoClass = pojo.getClass();
		if (pojoClass.isArray()) {
			if (null == annotations) {
				annotations = new ArrayList<Annotation>();
			}
			String attributeName = null;
			fillArray(pojo, attributeName,
					pojoClass.getClass().getComponentType(),
					pojoClass.getClass().getComponentType(),
					annotations,
					manufacturingCtx);
		} else if (pojo instanceof Collection) {
			@SuppressWarnings("unchecked")
			Collection<Object> collection = (Collection<Object>) pojo;
			AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
					PodamConstants.NO_TYPES);
			Class<?> elementTypeClass = findInheretedCollectionElementType(collection,
					manufacturingCtx, elementGenericTypeArgs, genericTypeArgs);
			if (null == annotations) {
				annotations = new ArrayList<Annotation>();
			}
			for (Annotation annotation : collection.getClass().getAnnotations()) {
				annotations.add(annotation);
			}
			String attributeName = null;
			fillCollection(manufacturingCtx, annotations, attributeName,
					collection, elementTypeClass, elementGenericTypeArgs.get());
		} else if (pojo instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<Object,Object> map = (Map<Object,Object>)pojo;
			MapArguments mapArguments = findInheretedMapElementType(
					map, manufacturingCtx, genericTypeArgs);
			if (null != annotations) {
				mapArguments.getAnnotations().addAll(annotations);
			}
			fillMap(mapArguments, manufacturingCtx);
		}

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

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

		for (ClassAttribute attribute : classAttributes) {

			if (!populateReadWriteField(pojo, attribute, manufacturingCtx)) {
				populateReadOnlyField(pojo, attribute, manufacturingCtx, genericTypeArgs);
			}
		}

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

				Object[] args = getParameterValuesForMethod(extraMethod, pojoClass,
						manufacturingCtx, genericTypeArgs);
				extraMethod.invoke(pojo, args);
			}
		}

		return pojo;
	}

	/**
	 * Fills a field with a getter
	 *
	 * @param <T>
	 *            The type for which should be populated
	 * @param pojo
	 *            The POJO being filled with values
	 * @param attribute
	 *            a attribute we are filling
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return true, if attribute was found and populated
	 * @throws ClassNotFoundException 
	 *              If class being manufactured cannot be loaded
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 */
	private <T> boolean populateReadOnlyField(T pojo, ClassAttribute attribute,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
					InvocationTargetException, ClassNotFoundException {

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

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

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

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

		if (fieldValue != null) {

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

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

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

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

					genericTypeArgsAll = genericTypeArgs;
				}

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

				manufacturingCtx.getPojos().put(fieldClass, depth + 1);
				populatePojoInternal(fieldValue, pojoAttributeAnnotations,
						manufacturingCtx, genericTypeArgsAll);
				manufacturingCtx.getPojos().put(fieldClass, depth);
				if (cloneTypeArgsMap) {
					manufacturingCtx.restoreTypeArgsMap();
				}
			} else {

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

			return false;
		}
	}

	/**
	 * Fills a field with a setter
	 *
	 * @param <T>
	 *            The type for which should be populated
	 * @param pojo
	 *            The POJO being filled with values
	 * @param attribute
	 *            a attribute we are filling
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @return true, if attribute was found and populated
	 * @throws ClassNotFoundException 
	 *              If class being manufactured cannot be loaded
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 */
	private <T> boolean populateReadWriteField(T pojo, ClassAttribute attribute,
			ManufacturingContext manufacturingCtx)
			throws InstantiationException, IllegalAccessException,
					InvocationTargetException, ClassNotFoundException {

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

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

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

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

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

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

		AttributeStrategy<?> attributeStrategy
				= TypeManufacturerUtil.findAttributeStrategy(strategy, pojoAttributeAnnotations, attributeType);
		if (null == attributeStrategy) {
			attributeStrategy = strategy.getStrategyForAttribute(attribute);
		}
		Object setterArg = null;
		if (null != attributeStrategy) {

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

		} else {

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

			final Type[] typeArguments;
			if (!(genericType instanceof GenericArrayType)) {
				attributeType = TypeManufacturerUtil.resolveGenericParameter(genericType,
						manufacturingCtx, typeGenericTypeArgs);
				typeArguments = typeGenericTypeArgs.get();
			} else {
				typeArguments = PodamConstants.NO_TYPES;
			}

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

			setterArg = manufactureAttributeValue(pojo, manufacturingCtx,
					attributeType, genericType,
					pojoAttributeAnnotations, attribute.getName(),
					typeArguments);
		}

		try {
			setter.invoke(pojo, setterArg);
		} catch(IllegalAccessException e) {
			LOG.warn("{} is not accessible. Setting it to accessible."
					+ " However this is a security hack and your code"
					+ " should really adhere to JavaBeans standards.",
					setter.toString());
			setter.setAccessible(true);
			setter.invoke(pojo, setterArg);
		}
		return true;
	}

	/**
	 * It manufactures and returns the value for a POJO attribute.
	 *
	 *
	 * @param pojo
	 *            The POJO being filled with values
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param attributeType
	 *            The type of the attribute for which a value is being
	 *            manufactured
	 * @param genericAttributeType
	 *            The generic type of the attribute for which a value is being
	 *            manufactured
	 * @param annotations
	 *            The annotations for the attribute being considered
	 * @param attributeName
	 *            The attribute name
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return The value for an attribute
	 *
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws IllegalArgumentException
	 *             <ul>
	 *             <li>If an illegal argument was passed</li>
	 *             <li>If an invalid value was set for a precise value in an
	 *             annotation and such value could not be converted to the
	 *             desired type</li>
	 *             </ul>
	 * @throws ClassNotFoundException
	 *              If class being manufactured cannot be loaded
	 *
	 */
	private Object manufactureAttributeValue(Object pojo,
			ManufacturingContext manufacturingCtx, Class<?> attributeType,
			Type genericAttributeType, List<Annotation> annotations,
			String attributeName,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

		Object attributeValue = null;

		Class<?> pojoClass = (pojo instanceof Class ? (Class<?>) pojo : pojo.getClass());
		Class<?> realAttributeType;
		AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(PodamConstants.NO_TYPES);
		if (attributeType != genericAttributeType
				&& Object.class.equals(attributeType)
				&& genericAttributeType instanceof TypeVariable) {
			realAttributeType = TypeManufacturerUtil.resolveGenericParameter(genericAttributeType,
                    manufacturingCtx, elementGenericTypeArgs);
		} else {
			realAttributeType = attributeType;
		}

		Type[] genericTypeArgsAll;
		if (elementGenericTypeArgs.get().length > 0) {
			genericTypeArgsAll = ArrayUtils.addAll(elementGenericTypeArgs.get(), genericTypeArgs);
		} else {
			genericTypeArgsAll = genericTypeArgs;
		}
		genericTypeArgsAll = TypeManufacturerUtil.mergeActualAndSuppliedGenericTypes(
					attributeType, genericAttributeType, genericTypeArgsAll, manufacturingCtx);

		AttributeMetadata attributeMetadata = new AttributeMetadata(
				attributeName, realAttributeType, genericAttributeType,
				genericTypeArgsAll, annotations, pojoClass, pojo);

		if (realAttributeType.isArray()) {

			// Array type

			attributeValue = resolveArrayElementValue(pojo, manufacturingCtx,
					attributeMetadata);

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

			attributeValue = resolveCollectionValueWhenCollectionIsPojoAttribute(
					pojo, manufacturingCtx, attributeMetadata);

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

			attributeValue = resolveMapValueWhenMapIsPojoAttribute(pojo,
					manufacturingCtx, attributeMetadata);

		}

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

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

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

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

			} else {

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

			}
		}

		return attributeValue;
	}

	/**
	 * Delegates POJO manufacturing to an external factory
	 *
	 * @param <T>
	 *            The type of the instance to return
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param msg
	 *            Message to log, must contain two parameters
	 * @param pojoClass
	 *            The class of which an instance is required
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @return instance of POJO produced by external factory or null
	 */
	private <T> T resortToExternalFactory(ManufacturingContext manufacturingCtx,
			String msg, Class<T> pojoClass,
			Type... genericTypeArgs) {

		LOG.warn(msg, pojoClass, externalFactory.getClass().getName());
		if (manufacturingCtx.getConstructorOrdering() == Order.HEAVY_FIRST) {
			return externalFactory.manufacturePojoWithFullData(pojoClass, genericTypeArgs);
		} else {
			return externalFactory.manufacturePojo(pojoClass, genericTypeArgs);
		}
	}




	/**
	 * It returns a collection of some sort with some data in it.
	 *
	 *
	 * @param pojo
	 *            The POJO being analyzed
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param attributeMetadata
	 *            The attribute's metadata
	 * @return a collection of some sort with some data in it
	 * @throws PodamMockeryException
	 *             An exception occurred while resolving the collection
	 * @throws IllegalArgumentException
	 *             If the field name is null or empty
	 */
	private Collection<? super Object> resolveCollectionValueWhenCollectionIsPojoAttribute(
			Object pojo, ManufacturingContext manufacturingCtx,
			AttributeMetadata attributeMetadata) {

		String attributeName = attributeMetadata.getAttributeName();

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

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

				if (fieldValue instanceof Collection) {
					@SuppressWarnings("unchecked")
					Collection<Object> tmp = (Collection<Object>)fieldValue;
					defaultValue = tmp;
				} else if (null != fieldValue) {
					LOG.warn("Obtained non-collection field default value {}", fieldValue.getClass());
				}
			}
		}

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

			@SuppressWarnings("unchecked")
			Class<Collection<Object>> collectionType
					= (Class<Collection<Object>>) attributeMetadata.getAttributeType();
			retValue = strategy.getTypeValue(attributeMetadata,
					manufacturingCtx, collectionType);
			if (null != retValue && null != defaultValue) {
				retValue.addAll(defaultValue);
			}
		}

		if (null == retValue) {
			return null;
		}

		try {

			Class<?> typeClass = null;

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

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

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

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

		} catch (SecurityException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		} catch (IllegalArgumentException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		} catch (InstantiationException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		} catch (IllegalAccessException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		} catch (ClassNotFoundException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		} catch (InvocationTargetException e) {
			throw new PodamMockeryException(RESOLVING_COLLECTION_EXCEPTION_STR,
					e);
		}

		return retValue;
	}

	/**
	 * Tries to find collection element type from collection object
	 *
	 * @param collection
	 *          The collection to be filled
	 * @param manufacturingCtx
	 *          the manufacturing context
	 * @param elementGenericTypeArgs
	 *          parameter to return generic arguments of collection element
	 * @param genericTypeArgs
	 *          The generic type arguments for the current generic class
	 *          instance
	 * @return
	 *        class type of collection element
	 */
	private Class<?> findInheretedCollectionElementType(
			Collection<Object> collection, ManufacturingContext manufacturingCtx,
			AtomicReference<Type[]> elementGenericTypeArgs,
			Type... genericTypeArgs) {

		Class<?> pojoClass = collection.getClass();
		Class<?> collectionClass = pojoClass;
		Type[] typeParams = collectionClass.getTypeParameters();
		main : while (typeParams.length < 1) {
			for (Type genericIface : collectionClass.getGenericInterfaces()) {
				Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
						genericIface, manufacturingCtx, elementGenericTypeArgs);
				if (Collection.class.isAssignableFrom(clazz)) {
					collectionClass = clazz;
					typeParams = elementGenericTypeArgs.get();
					continue main;
				}
			}
			Type type = collectionClass.getGenericSuperclass();
			if (type != null) {
				Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
						type, manufacturingCtx, elementGenericTypeArgs);
				if (Collection.class.isAssignableFrom(clazz)) {
					collectionClass = clazz;
					typeParams = elementGenericTypeArgs.get();
					continue main;
				}
			}
			if (Collection.class.equals(collectionClass)) {
				LOG.warn("Collection {} doesn't have generic types,"
						+ "will use Object instead", pojoClass);
				typeParams = new Type[] { Object.class };
			}
		}
		Class<?> elementTypeClass = TypeManufacturerUtil.resolveGenericParameter(typeParams[0],
					manufacturingCtx, elementGenericTypeArgs);
		Type[] elementGenericArgs = ArrayUtils.addAll(
				elementGenericTypeArgs.get(), genericTypeArgs);
		elementGenericTypeArgs.set(elementGenericArgs);
		return elementTypeClass;
	}

	/**
	 * It fills a collection with the required number of elements of the
	 * required type.
	 *
	 * <p>
	 * This method has a so-called side effect. It updates the collection passed
	 * as argument.
	 * </p>
	 *
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param annotations
	 *            The annotations for this attribute
	 * @param attributeName
	 *            The attribute name of collection in enclosing POJO class 
	 * @param collection
	 *            The Collection to be filled
	 * @param collectionElementType
	 *            The type of the collection element
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 *
	 */
	private void fillCollection(ManufacturingContext manufacturingCtx,
			List<Annotation> annotations, String attributeName,
			Collection<? super Object> collection,
			Class<?> collectionElementType, Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

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

				collection.clear();
			}

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

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

				if (null == element) {

					manufacturingCtx.backupTypeArgsMap(manufacturingCtx.createEmptyTypeArgsMap());
					element = manufactureAttributeValue(collection, manufacturingCtx,
							collectionElementType, collectionElementType,
							annotations, attributeName, genericTypeArgs);
					manufacturingCtx.restoreTypeArgsMap();
				}

				if (null != element) {
					collection.add(element);
				}
			}
		} catch (UnsupportedOperationException e) {

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

	/**
	 * It manufactures and returns a Map with at least one element in it
	 *
	 * @param pojo
	 *            The POJO being initialized
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param attributeMetadata
	 *            The attribute's metadata
	 * @return Map with at least one element in it
	 *
	 * @throws IllegalArgumentException
	 *             <ul>
	 *             <li>If the attribute name is null or empty</li>
	 *             <li>If the array of types of the Map has length different
	 *             from 2</li>
	 *             </ul>
	 *
	 * @throws PodamMockeryException
	 *             If an error occurred while creating the Map object
	 */
	private Map<? super Object, ? super Object> resolveMapValueWhenMapIsPojoAttribute(
			Object pojo, ManufacturingContext manufacturingCtx,
			AttributeMetadata attributeMetadata) {

		String attributeName = attributeMetadata.getAttributeName();

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

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

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

			@SuppressWarnings("unchecked")
			Class<Map<Object,Object>> mapType
					= (Class<Map<Object, Object>>) attributeMetadata.getAttributeType();
			retValue = strategy.getTypeValue(attributeMetadata, manufacturingCtx, mapType);
			if (null != retValue && null != defaultValue) {
				retValue.putAll(defaultValue);
			}
		}

		if (null == retValue) {
			return null;
		}

		try {

			Class<?> keyClass = null;

			Class<?> elementClass = null;

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

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

				keyClass = mapArgs.getKeyOrValueType();

				elementClass = mapArgs.getElementClass();

			} else {

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

				Type[] actualTypeArguments = attributeMetadata.getAttrGenericArgs();
				keyClass = TypeManufacturerUtil.resolveGenericParameter(actualTypeArguments[0],
						manufacturingCtx, keyGenericTypeArgs);
				elementClass = TypeManufacturerUtil.resolveGenericParameter(actualTypeArguments[1],
						manufacturingCtx, elementGenericTypeArgs);
			}

			MapArguments mapArguments = new MapArguments();
			mapArguments.setAttributeName(attributeName);
			mapArguments.getAnnotations().addAll(attributeMetadata.getAttributeAnnotations());
			mapArguments.setMapToBeFilled(retValue);
			mapArguments.setKeyOrValueType(keyClass);
			mapArguments.setElementClass(elementClass);
			mapArguments.setKeyGenericTypeArgs(keyGenericTypeArgs.get());
			mapArguments
					.setElementGenericTypeArgs(elementGenericTypeArgs.get());

			fillMap(mapArguments, manufacturingCtx);

		} catch (InstantiationException e) {
			throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
		} catch (IllegalAccessException e) {
			throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
		} catch (SecurityException e) {
			throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
		} catch (ClassNotFoundException e) {
			throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
		} catch (InvocationTargetException e) {
			throw new PodamMockeryException(MAP_CREATION_EXCEPTION_STR, e);
		}

		return retValue;
	}

	/**
	 * Finds key and element type arguments 
	 *
	 * @param map
	 *          The map being initialized
	 * @param manufacturingCtx
	 *          the manufacturing context
	 * @param genericTypeArgs
	 *          The generic type arguments for the current generic class
	 *          instance
	 * @return
	 *        Inherited map key and element types
	 *
	 */
	private MapArguments findInheretedMapElementType(Map<Object, Object> map,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs) {

		Class<?> pojoClass = map.getClass();
		Class<?> mapClass = pojoClass;
		AtomicReference<Type[]> elementGenericTypeArgs = new AtomicReference<Type[]>(
				PodamConstants.NO_TYPES);
		Type[] typeParams = mapClass.getTypeParameters();
		main : while (typeParams.length < 2) {
			for (Type genericIface : mapClass.getGenericInterfaces()) {
				Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
						genericIface, manufacturingCtx, elementGenericTypeArgs);
				if (Map.class.isAssignableFrom(clazz)) {
					typeParams = elementGenericTypeArgs.get();
					mapClass = clazz;
					continue main;
				}
			}
			Type type = mapClass.getGenericSuperclass();
			if (type != null) {
				Class<?> clazz = TypeManufacturerUtil.resolveGenericParameter(
						type, manufacturingCtx, elementGenericTypeArgs);
				if (Map.class.isAssignableFrom(clazz)) {
					typeParams = elementGenericTypeArgs.get();
					mapClass = clazz;
					continue main;
				}
			}
			if (Map.class.equals(mapClass)) {
				LOG.warn("Map {} doesn't have generic types,"
						+ "will use Object, Object instead", pojoClass);
				typeParams = new Type[] { Object.class, Object.class };
			}
		}
		AtomicReference<Type[]> keyGenericTypeArgs = new AtomicReference<Type[]>(
				PodamConstants.NO_TYPES);
		Class<?> keyClass = TypeManufacturerUtil.resolveGenericParameter(typeParams[0],
					manufacturingCtx, keyGenericTypeArgs);
		Class<?> elementClass = TypeManufacturerUtil.resolveGenericParameter(
				typeParams[1], manufacturingCtx, elementGenericTypeArgs);

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

		MapArguments mapArguments = new MapArguments();
		for (Annotation annotation : pojoClass.getAnnotations()) {
			mapArguments.getAnnotations().add(annotation);
		}
		mapArguments.setMapToBeFilled(map);
		mapArguments.setKeyOrValueType(keyClass);
		mapArguments.setElementClass(elementClass);
		mapArguments.setKeyGenericTypeArgs(keyGenericArgs);
		mapArguments.setElementGenericTypeArgs(elementGenericArgs);

		return mapArguments;
	}

	/**
	 * It fills a Map with the required number of elements of the required type.
	 *
	 * <p>
	 * This method has a so-called side-effect. It updates the Map given as
	 * argument.
	 * </p>
	 *
	 * @param mapArguments
	 *             The arguments POJO
	 * @param manufacturingCtx
	 *             Manufacturing context
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 *
	 */
	private void fillMap(MapArguments mapArguments, ManufacturingContext manufacturingCtx)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

		// If the user defined a strategy to fill the collection elements,
		// we use it
		Holder<AttributeStrategy<?>> elementStrategyHolder
				= new Holder<AttributeStrategy<?>>();
		Holder<AttributeStrategy<?>> keyStrategyHolder
				= new Holder<AttributeStrategy<?>>();
		Integer nbrElements = TypeManufacturerUtil.findCollectionSize(strategy, mapArguments.getAnnotations(),
                mapArguments.getElementClass(), elementStrategyHolder,
                keyStrategyHolder);
		AttributeStrategy<?> keyStrategy = keyStrategyHolder.getValue();
		AttributeStrategy<?> elementStrategy = elementStrategyHolder.getValue();

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

				map.clear();
			}

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

				Object keyValue = null;

				Object elementValue = null;

				MapKeyOrElementsArguments valueArguments = new MapKeyOrElementsArguments();
				valueArguments.setAttributeName(mapArguments.getAttributeName());
				valueArguments.setMapToBeFilled(mapArguments.getMapToBeFilled());
				valueArguments.getAnnotations().addAll(mapArguments.getAnnotations());
				valueArguments.setKeyOrValueType(mapArguments.getKeyOrValueType());
				valueArguments.setElementStrategy(keyStrategy);
				valueArguments.setGenericTypeArgs(mapArguments
						.getKeyGenericTypeArgs());

				keyValue = getMapKeyOrElementValue(valueArguments, manufacturingCtx);

				valueArguments.setKeyOrValueType(mapArguments.getElementClass());
				valueArguments.setElementStrategy(elementStrategy);
				valueArguments.setGenericTypeArgs(mapArguments
						.getElementGenericTypeArgs());

				elementValue = getMapKeyOrElementValue(valueArguments, manufacturingCtx);

				if (elementValue != null) {
					map.put(keyValue, elementValue);
				}
			}
		} catch (UnsupportedOperationException e) {

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

	/**
	 * It fills a Map key or value with the appropriate value, considering
	 * attribute-level customisation.
	 *
	 * @param keyOrElementsArguments
	 *            The arguments POJO
	 * @param manufacturingCtx
	 *             manufacturing context
	 * @return A Map key or value
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws IllegalArgumentException
	 *             <ul>
	 *             <li>If an illegal argument was passed</li>
	 *             <li>If an invalid value was set for a precise value in an
	 *             annotation and such value could not be converted to the
	 *             desired type</li>
	 *             </ul>
	 * @throws ClassNotFoundException
	 *             If manufactured class could not be loaded
	 */
	private Object getMapKeyOrElementValue(
			MapKeyOrElementsArguments keyOrElementsArguments,
			ManufacturingContext manufacturingCtx)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

		AttributeStrategy<?> strategy = keyOrElementsArguments.getElementStrategy();
		Object retValue = TypeManufacturerUtil.returnAttributeDataStrategyValue(
					keyOrElementsArguments.getKeyOrValueType(),
					keyOrElementsArguments.getAnnotations(),
					strategy);

		if (null == retValue) {

			manufacturingCtx.backupTypeArgsMap(manufacturingCtx.createEmptyTypeArgsMap());
			retValue = manufactureAttributeValue(
					keyOrElementsArguments.getMapToBeFilled(),
					manufacturingCtx,
					keyOrElementsArguments.getKeyOrValueType(),
					keyOrElementsArguments.getKeyOrValueType(),
					keyOrElementsArguments.getAnnotations(),
					keyOrElementsArguments.getAttributeName(),
					keyOrElementsArguments.getGenericTypeArgs());
			manufacturingCtx.restoreTypeArgsMap();
		}
		return retValue;
	}

	/**
	 * It fills an Array with the required number of elements of the required type.
	 *
	 * <p>
	 * This method has a so-called side-effect. It updates the Map given as
	 * argument.
	 * </p>
	 *
	 * @param array
	 *             The array POJO
	 * @param attributeName
	 *            The attribute name of collection in enclosing POJO class 
	 * @param elementType
	 *            The generic type of the collection element
	 * @param genericElementType
	 *            The generic type of the collection element
	 * @param annotations
	 *            The annotations for this attribute
	 * @param manufacturingCtx
	 *             Manufacturing context
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 *
	 */
	private void fillArray(Object array, String attributeName, Class<?> elementType,
			Type genericElementType, List<Annotation> annotations,
			ManufacturingContext manufacturingCtx)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

		Class<?> componentType = array.getClass().getComponentType();
		Type genericComponentType;
		AtomicReference<Type[]> genericTypeArgs = new AtomicReference<Type[]>(
				PodamConstants.NO_TYPES);
		if (genericElementType instanceof GenericArrayType) {
			genericComponentType = ((GenericArrayType) genericElementType).getGenericComponentType();
			if (genericComponentType instanceof TypeVariable) {
				TypeVariable<?> componentTypeVariable
						 = (TypeVariable<?>) genericComponentType;
				final Type resolvedType
						 = manufacturingCtx.resolveType(componentTypeVariable.getName());
				componentType
						 = TypeManufacturerUtil.resolveGenericParameter(resolvedType,
						 		manufacturingCtx, genericTypeArgs);
			}
		} else {
			genericComponentType = componentType;
		}

		// If the user defined a strategy to fill the collection elements,
		// we use it
		Holder<AttributeStrategy<?>> elementStrategyHolder
				= new Holder<AttributeStrategy<?>>();
		Holder<AttributeStrategy<?>> keyStrategyHolder = null;
		TypeManufacturerUtil.findCollectionSize(strategy,
				annotations, elementType,
				elementStrategyHolder, keyStrategyHolder);
		AttributeStrategy<?> elementStrategy = elementStrategyHolder.getValue();

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

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

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

				if (null == arrayElement) {
					arrayElement = manufactureAttributeValue(array, manufacturingCtx,
							componentType, genericComponentType,
							annotations, attributeName,
							genericTypeArgs.get());
				}

				Array.set(array, i, arrayElement);
			}
		}
	}

	/**
	 * It returns an Array with the first element set
	 *
	 *
	 * @param pojo
	 *            POJO containing attribute
	 * @param manufacturingCtx
	 *          the manufacturing context
	 * @param attributeMetadata
	 *            The attribute's metadata
	 * @return Array with the first element set
	 * @throws IllegalArgumentException
	 *             If an illegal argument was passed to the constructor
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 */
	private Object resolveArrayElementValue(Object pojo,
			ManufacturingContext manufacturingCtx,
			AttributeMetadata attributeMetadata) throws InstantiationException,
			IllegalAccessException, InvocationTargetException,
			ClassNotFoundException {

		@SuppressWarnings("unchecked")
		Class<Object> arrayType
				= (Class<Object>) attributeMetadata.getAttributeType();
		Object array = strategy.getTypeValue(attributeMetadata, manufacturingCtx, arrayType);
		fillArray(array, attributeMetadata.getAttributeName(),
				attributeMetadata.getAttributeType(),
				attributeMetadata.getAttributeGenericType(),
				attributeMetadata.getAttributeAnnotations(),
				manufacturingCtx);
		return array;
	}


	/**
	 * Given a constructor it manufactures and returns the parameter values
	 * required to invoke it
	 *
	 * @param constructor
	 *            The constructor for which parameter values are required
	 * @param pojoClass
	 *            The POJO class containing the constructor
	 * @param manufacturingCtx
	 *          the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 *
	 * @return The parameter values required to invoke the constructor
	 * @throws IllegalArgumentException
	 *             If an illegal argument was passed to the constructor
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 */
	private Object[] getParameterValuesForConstructor(
			Constructor<?> constructor, Class<?> pojoClass,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

		if (parameterTypes.length == 0) {

			return PodamConstants.NO_ARGS;

		} else {

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

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

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

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

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

				parameterValues[idx] = manufactureParameterValue(pojoClass,
						idx + ctorName, parameterTypes[idx], genericType,
						annotations, manufacturingCtx, genericTypeArgs);
			}
			return parameterValues;
		}
	}

	/**
	 * Given a method it manufactures and returns the parameter values
	 * required to invoke it
	 *
	 * @param method
	 *            The method for which parameter values are required
	 * @param pojoClass
	 *            The POJO class containing the constructor
	 * @param manufacturingCtx
	 *          the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 *
	 * @return The parameter values required to invoke the method
	 * @throws IllegalArgumentException
	 *             If an illegal argument was passed to the method
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 */
	private Object[] getParameterValuesForMethod(
			Method method, Class<?> pojoClass,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

		if (parameterTypes.length == 0) {

			return PodamConstants.NO_ARGS;

		} else {

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

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

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

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

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

				parameterValues[idx] = manufactureParameterValue(pojoClass,
						idx + methodName, parameterTypes[idx], genericType,
						annotations, manufacturingCtx, genericTypeArgs);
			}
			return parameterValues;
		}
	}

	/**
	 * Manufactures and returns the parameter value for method required to
	 * invoke it
	 *
	 * @param pojoClass pojo class
	 * @param parameterName name of parameter
	 * @param parameterType type of parameter
	 * @param genericType generic type of parameter
	 * @param annotations parameter annotations
	 * @param manufacturingCtx
	 *            the manufacturing context
	 * @param genericTypeArgs
	 *            The generic type arguments for the current generic class
	 *            instance
	 *
	 * @return The parameter values required to invoke the constructor
	 * @throws IllegalArgumentException
	 *             If an illegal argument was passed to the constructor
	 * @throws InstantiationException
	 *             If an exception occurred during instantiation
	 * @throws IllegalAccessException
	 *             If security was violated while creating the object
	 * @throws InvocationTargetException
	 *             If an exception occurred while invoking the constructor or
	 *             factory method
	 * @throws ClassNotFoundException
	 *             If it was not possible to create a class from a string
	 */
	private Object manufactureParameterValue(Class<?> pojoClass,
			String parameterName, Class<?> parameterType, Type genericType,
			final List<Annotation> annotations,
			ManufacturingContext manufacturingCtx,
			Type... genericTypeArgs)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, ClassNotFoundException {

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

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

		final boolean cloneTypeArgsMap = (genericType instanceof ParameterizedType);
		Type[] genericTypeArgsAll;
		if (cloneTypeArgsMap) {
			genericTypeArgsAll = manufacturingCtx.cloneTypeArgsMap(
					parameterType, (ParameterizedType) genericType, genericTypeArgs);
		} else {
			genericTypeArgsAll = genericTypeArgs;
		}

		Object retValue = manufactureAttributeValue(pojoClass, manufacturingCtx, parameterType,
				genericType, annotations, parameterName,
				genericTypeArgsAll);
		if (cloneTypeArgsMap) {
			manufacturingCtx.restoreTypeArgsMap();
		}
		return retValue;
	}

    /**
     * Returns a value for an abstract type or interface if possible.
     * @param pojoClass The Pojo class
     * @param pojoMetadata The Pojo metadata
     * @param manufacturingCtx The manufacturing context
     * @param genericTypeArgs The generic type arguments map
     * @param <T> The type of the value to be returned
     * @return a value or null, if manufacturing didn't succeed
     * @throws InstantiationException If a problem occurred while instantiating the object
     * @throws IllegalAccessException If a problem occurred while instantiating the object
     * @throws InvocationTargetException If a problem occurred while instantiating the object
     * @throws ClassNotFoundException If a problem occurred while instantiating the object
     */
    private <T> T getValueForAbstractType(Class<T> pojoClass,
                                          AttributeMetadata pojoMetadata,
                                          ManufacturingContext manufacturingCtx,
                                          Type[] genericTypeArgs)
            throws InstantiationException, IllegalAccessException,
            InvocationTargetException, ClassNotFoundException {

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

        if (!specificClass.equals(pojoClass)) {

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

        Class<?> factory = strategy.getFactoryClass(pojoClass);
        if (factory != null) {
            T retValue = instantiatePojoWithFactory(factory, pojoClass,
                manufacturingCtx, genericTypeArgs);
            if (retValue != null) {
                return retValue;
            }
        }

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


}