package at.ac.tuwien.dsg.scaledom.util; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.reflect.ConstructorUtils; /** * A simple component factory.<br/> * Users may either: * <ul> * <li>bind an instance to a type, or</li> * <li>bind an implementation type to a type</li> * </ul> * If instances are bound, the user may request the bound instance any time by calling {@link #getInstance(Class)}. If * an implementation type is bound, the user may request a new instance of that implementation type by calling * {@link #getNewInstance(Class, Object...)}, providing possible constructor parameters as ellipsis arguments. * * @author Dominik Rauch */ public class ComponentFactory { /** Bound instances. */ private final Map<Class<?>, Object> instances; /** Bound implementation classes. */ private final Map<Class<?>, Class<?>> bindings; /** * Default constructor. */ public ComponentFactory() { instances = new HashMap<Class<?>, Object>(); bindings = new HashMap<Class<?>, Class<?>>(); } /** * Binds an existing instance to a type. * * @param type the type. * @param instance the instance. */ public <T, U extends T> void bind(final Class<T> type, final U instance) { checkNotNull(type, "Argument type must not be null."); checkNotNull(instance, "Argument instance must not be null."); instances.put(type, instance); } /** * Binds a new instance to a type. * * @param type the type. * @param implType the implementation type. * @param args possible constructor arguments. * @throws InstantiationException If the instantiation of the new instance fails. */ public <T> void bind(final Class<T> type, final Class<? extends T> implType, final Object... args) throws InstantiationException { bind(type, getNewInstanceInternal(implType, args)); } /** * Binds an implementation type to a type. * * @param type the type. * @param implType the implementation type. */ public <T> void bind(final Class<T> type, final Class<? extends T> implType) { checkNotNull(type, "Argument type must not be null."); checkNotNull(implType, "Argument implType must not be null."); bindings.put(type, implType); } /** * Returns the bound instance. * * @param type the type. * @return the bound instance. * @throws IllegalArgumentException If no instance is bound to the given type. */ public <T> T getInstance(final Class<T> type) { checkNotNull(type, "Argument type must not be null."); checkArgument(instances.containsKey(type), "No instance is bound to type '%s'.", type.getName()); @SuppressWarnings("unchecked") final T instance = (T) instances.get(type); return instance; } /** * Returns a new instance of the bound implementation type as a reference to type. * * @param type the type. * @param args possible constructor parameters. * @return the new instance. * @throws IllegalArgumentException If no implementation type is bound to the given type. * @throws InstantiationException If the instantiation of the new instance fails. */ public <T> T getNewInstance(final Class<T> type, final Object... args) throws InstantiationException { checkNotNull(type, "Argument type must not be null."); checkArgument(bindings.containsKey(type), "No implementation class is bound to type '%s'.", type.getName()); @SuppressWarnings("unchecked") final Class<T> implType = (Class<T>) bindings.get(type); return getNewInstanceInternal(implType, args); } private <T> T getNewInstanceInternal(final Class<? extends T> implType, final Object... args) throws InstantiationException { try { return ConstructorUtils.invokeConstructor(implType, args); } catch (final IllegalAccessException ex) { throw new InstantiationException("Type '" + implType.getName() + "' with arguments " + Arrays.asList(args) + " could not be instantiated: " + ex.getMessage()); } catch (final InvocationTargetException ex) { throw new InstantiationException("Type '" + implType.getName() + "' with arguments " + Arrays.asList(args) + " could not be instantiated: " + ex.getMessage()); } catch (final NoSuchMethodException ex) { throw new InstantiationException("Type '" + implType.getName() + "' with arguments " + Arrays.asList(args) + " could not be instantiated: " + ex.getMessage()); } } }