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     * An 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    
036        private Class key;
037    
038        public FactoryInjector() throws PicoCompositionException {
039            key = getTypeArguments(FactoryInjector.class, getClass()).get(0);
040            if (key == null) {
041                key = CantWorkItOut.class;
042            }
043        }
044    
045        public FactoryInjector(Class<T> key) {
046            this.key = key;
047        }
048    
049        // from http://www.artima.com/weblogs/viewpost.jsp?thread=208860
050        public static Class<?> getClass(Type type) {
051            if (type instanceof Class) {
052                return (Class) type;
053            } else if (type instanceof ParameterizedType) {
054                return getClass(((ParameterizedType) type).getRawType());
055            } else if (type instanceof GenericArrayType) {
056                Type componentType = ((GenericArrayType) type).getGenericComponentType();
057                Class<?> componentClass = getClass(componentType);
058                if (componentClass != null) {
059                    return Array.newInstance(componentClass, 0).getClass();
060                } else {
061                    return null;
062                }
063            } else {
064                return null;
065            }
066        }
067    
068        /**
069       * Get the actual type arguments a child class has used to extend a generic base class.
070       *
071       * @param class1 the base class
072       * @param class2 the child class
073       * @return a list of the raw classes for the actual type arguments.
074       */
075      public static <T> List<Class<?>> getTypeArguments(
076        Class<FactoryInjector> class1, Class<? extends Object> class2) {
077        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
078        Type type = class2;
079        // start walking up the inheritance hierarchy until we hit baseClass
080        while (! getClass(type).equals(class1)) {
081          if (type instanceof Class) {
082            // there is no useful information for us in raw types, so just keep going.
083            type = ((Class) type).getGenericSuperclass();
084          }
085          else {
086            ParameterizedType parameterizedType = (ParameterizedType) type;
087            Class<?> rawType = (Class) parameterizedType.getRawType();
088    
089            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
090            TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
091            for (int i = 0; i < actualTypeArguments.length; i++) {
092              resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
093            }
094    
095            if (!rawType.equals(class1)) {
096              type = rawType.getGenericSuperclass();
097            }
098          }
099        }
100    
101        // finally, for each actual type argument provided to baseClass, determine (if possible)
102        // the raw class for that type argument.
103        Type[] actualTypeArguments;
104        if (type instanceof Class) {
105          actualTypeArguments = ((Class) type).getTypeParameters();
106        }
107        else {
108          actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
109        }
110        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
111        // resolve types by chasing down type variables.
112        for (Type baseType: actualTypeArguments) {
113          while (resolvedTypes.containsKey(baseType)) {
114            baseType = resolvedTypes.get(baseType);
115          }
116          typeArgumentsAsClasses.add(getClass(baseType));
117        }
118        return typeArgumentsAsClasses;
119      }
120    
121        public Object getComponentKey() {
122            return key;
123        }
124    
125        public Class<T> getComponentImplementation() {
126            return key;
127        }
128    
129        public void accept(PicoVisitor visitor) {
130            visitor.visitComponentAdapter(this);
131        }
132    
133        public ComponentAdapter<T> getDelegate() {
134            return null;
135        }
136    
137        public <U extends ComponentAdapter> U findAdapterOfType(Class<U> adapterType) {
138            return null;
139        }
140    
141        public T getComponentInstance(PicoContainer container) {
142            throw new UnsupportedOperationException();
143        }
144    
145        public abstract T getComponentInstance(PicoContainer container, Type into);
146    
147        public Object decorateComponentInstance(PicoContainer container, Type into, T instance) {
148            return null;
149        }
150    
151        public void verify(PicoContainer container) {
152        }
153    
154        public String getDescriptor() {
155            return "FactoryInjector-";
156        }
157    
158        public void start(PicoContainer container) {
159        }
160    
161        public void stop(PicoContainer container) {
162        }
163    
164        public void dispose(PicoContainer container) {
165        }
166    
167        public boolean componentHasLifecycle() {
168            return false;
169        }
170    
171        public static class CantWorkItOut {
172            private CantWorkItOut() {
173            }
174        }
175    
176    }