001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     *****************************************************************************/
009    package org.picocontainer.injectors;
010    
011    import java.lang.reflect.ParameterizedType;
012    import java.lang.reflect.Type;
013    import java.lang.reflect.Array;
014    import java.lang.reflect.GenericArrayType;
015    import java.lang.reflect.TypeVariable;
016    import java.util.List;
017    import java.util.ArrayList;
018    import java.util.HashMap;
019    import java.util.Map;
020    
021    import org.picocontainer.ComponentAdapter;
022    import org.picocontainer.Injector;
023    import org.picocontainer.PicoCompositionException;
024    import org.picocontainer.PicoContainer;
025    import org.picocontainer.PicoVisitor;
026    
027    /**
028     * <p>
029     * A Injector which provides an custom instance in a factory style
030     * </p>
031     *
032     * @author Paul Hammant
033     */
034    public abstract class FactoryInjector<T> implements Injector<T> {
035        private Class key;
036    
037        public FactoryInjector() throws PicoCompositionException {
038            key = getTypeArguments(FactoryInjector.class, getClass()).get(0);
039            if (key == null) {
040                key = CantWorkItOut.class;
041            }
042        }
043    
044        public FactoryInjector(Class<T> key) {
045            this.key = key;
046        }
047    
048        // from http://www.artima.com/weblogs/viewpost.jsp?thread=208860
049        public static Class<?> getClass(Type type) {
050            if (type instanceof Class) {
051                return (Class) type;
052            } else if (type instanceof ParameterizedType) {
053                return getClass(((ParameterizedType) type).getRawType());
054            } else if (type instanceof GenericArrayType) {
055                Type componentType = ((GenericArrayType) type).getGenericComponentType();
056                Class<?> componentClass = getClass(componentType);
057                if (componentClass != null) {
058                    return Array.newInstance(componentClass, 0).getClass();
059                } else {
060                    return null;
061                }
062            } else {
063                return null;
064            }
065        }
066    
067        /**
068       * Get the actual type arguments a child class has used to extend a generic base class.
069       *
070       * @param class1 the base class
071       * @param class2 the child class
072       * @return a list of the raw classes for the actual type arguments.
073       */
074      public static <T> List<Class<?>> getTypeArguments(
075        Class<FactoryInjector> class1, Class<? extends Object> class2) {
076        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
077        Type type = class2;
078        // start walking up the inheritance hierarchy until we hit baseClass
079        while (! getClass(type).equals(class1)) {
080          if (type instanceof Class) {
081            // there is no useful information for us in raw types, so just keep going.
082            type = ((Class) type).getGenericSuperclass();
083          }
084          else {
085            ParameterizedType parameterizedType = (ParameterizedType) type;
086            Class<?> rawType = (Class) parameterizedType.getRawType();
087    
088            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
089            TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
090            for (int i = 0; i < actualTypeArguments.length; i++) {
091              resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
092            }
093    
094            if (!rawType.equals(class1)) {
095              type = rawType.getGenericSuperclass();
096            }
097          }
098        }
099    
100        // finally, for each actual type argument provided to baseClass, determine (if possible)
101        // the raw class for that type argument.
102        Type[] actualTypeArguments;
103        if (type instanceof Class) {
104          actualTypeArguments = ((Class) type).getTypeParameters();
105        }
106        else {
107          actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
108        }
109        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
110        // resolve types by chasing down type variables.
111        for (Type baseType: actualTypeArguments) {
112          while (resolvedTypes.containsKey(baseType)) {
113            baseType = resolvedTypes.get(baseType);
114          }
115          typeArgumentsAsClasses.add(getClass(baseType));
116        }
117        return typeArgumentsAsClasses;
118      }
119    
120        public Object getComponentKey() {
121            return key;
122        }
123    
124        public Class<T> getComponentImplementation() {
125            return key;
126        }
127    
128        public void accept(PicoVisitor visitor) {
129            visitor.visitComponentAdapter(this);
130        }
131    
132        public ComponentAdapter<T> getDelegate() {
133            return null;
134        }
135    
136        public <U extends ComponentAdapter> U findAdapterOfType(Class<U> componentAdapterType) {
137            return null;
138        }
139    
140        public T getComponentInstance(PicoContainer container) {
141            throw new UnsupportedOperationException();
142        }
143    
144        public abstract T getComponentInstance(PicoContainer container, Type clazz);
145    
146        public void decorateComponentInstance(PicoContainer container, Type into, T instance) {
147        }
148    
149    
150        public void verify(PicoContainer container) {
151        }
152    
153        public String getDescriptor() {
154            return "FactoryInjector-";
155        }
156    
157        public void start(PicoContainer container) {
158        }
159    
160        public void stop(PicoContainer container) {
161        }
162    
163        public void dispose(PicoContainer container) {
164        }
165    
166        public boolean componentHasLifecycle() {
167            return false;
168        }
169    
170        public static class CantWorkItOut {}
171    
172    }