package com.ikokoon.toolkit; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.log4j.Logger; /** * This is an abstract factory. Classes are selected for construction according to the best match between the parameters and the solid implementation * of the class. * * @author Michael Couck * @since 02.09.08 * @version 01.00 */ public abstract class ObjectFactory { /** The logger for the class. */ private static Logger LOGGER = Logger.getLogger(ObjectFactory.class); /** * This method instantiates a class based on the solid implementation class passed as a parameter and the parameters. A best match between the two * parameters determines the class to be instanciated. * * @param <E> * the desired class to be instanciated * @param klass * the class to be instanciated * @param allParameters * the parameters for the constructor, these cannot be primitives and the parameters in the constructor have to be objects as well, not * primitives * @return the class that best matches the desired class and the parameters for constructors */ public static <E> E getObject(Class<E> klass, Object... allParameters) { List<Object> parameters = new ArrayList<Object>(); Constructor<E> constructor = getConstructor(klass, allParameters, parameters); if (constructor != null) { if (!constructor.isAccessible()) { constructor.setAccessible(true); } try { return constructor.newInstance(parameters.toArray()); } catch (IllegalArgumentException e) { LOGGER.error("Exception generating the action for " + klass + ", with parameters : " + Arrays.asList(allParameters), e); } catch (InstantiationException e) { LOGGER.error("Exception generating the action for " + klass + ", with parameters : " + Arrays.asList(allParameters), e); } catch (IllegalAccessException e) { LOGGER.error("Exception generating the action for " + klass + ", with parameters : " + Arrays.asList(allParameters), e); } catch (InvocationTargetException e) { LOGGER.error("Exception generating the action for " + klass + ", with parameters : " + Arrays.asList(allParameters), e); } } return null; } /** * Finds a constructor in a class that has a signature that includes some the parameters in the parameter list on a best match principal. Note * that this method will return the first constructor that has all the parameters in one of the permutations even if there is another constructor * that has more parameters in another of the permutations. * * @param klass * the class look for a constructor in * @param allParameters * all the parameters that are available for the constructor * @param parameters * the parameters that were collected for the best match constructor * @return the constructor that has all the parameters */ protected static <E> Constructor<E> getConstructor(Class<E> klass, Object[] allParameters, List<Object> parameters) { Constructor<E> constructor = getConstructor(klass, allParameters); if (constructor != null) { parameters.addAll(Arrays.asList(allParameters)); LOGGER.debug("Got constructor : " + constructor + ", with parameters : " + parameters); return constructor; } Permutations permutations = new Permutations(); List<Object[]> permutationsList = new ArrayList<Object[]>(); permutations.getPermutations(allParameters, permutationsList, allParameters.length); for (Object[] permutationParameters : permutationsList) { LOGGER.debug("Permutations : " + Arrays.asList(permutationParameters)); constructor = getConstructor(klass, permutationParameters); if (constructor != null) { parameters.addAll(Arrays.asList(allParameters)); LOGGER.debug("Got constructor : " + constructor + ", with parameters : " + parameters); return constructor; } // Try every possible permutation of the parameters, we ignore nulls for (int first = 0; first < permutationParameters.length; first++) { for (int last = first; last < permutationParameters.length; last++) { int size = last - first; LOGGER.debug("First : " + first + ", last : " + last + ", size : " + size); Object[] dest = new Object[size]; System.arraycopy(permutationParameters, first, dest, 0, size); constructor = getConstructor(klass, dest); if (constructor != null) { parameters.addAll(Arrays.asList(dest)); LOGGER.debug("Got constructor : " + constructor + ", with parameters : " + parameters); return constructor; } } } } return null; } @SuppressWarnings("unchecked") private static <E> Constructor<E> getConstructor(Class<E> klass, Object[] permutationParameters) { LOGGER.debug("Stripped permutations : " + Arrays.asList(permutationParameters)); Constructor<?>[] constructors = klass.getDeclaredConstructors(); outer: for (Constructor<?> constructor : constructors) { LOGGER.debug("Looking at constructor : " + constructor + " for class " + klass); Class<?>[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes != null && parameterTypes.length != permutationParameters.length) { continue; } int index = -1; for (Class<?> parameterType : parameterTypes) { index++; if (permutationParameters[index] == null) { continue outer; } if (!parameterType.isAssignableFrom(permutationParameters[index].getClass())) { continue outer; } } return (Constructor<E>) constructor; } return null; } /** * Private constructor ensures singularity. */ private ObjectFactory() { } }