001    /**
002     Licensed to the Apache Software Foundation (ASF) under one or more
003     contributor license agreements.  See the NOTICE file distributed with
004     this work for additional information regarding copyright ownership.
005     The ASF licenses this file to You under the Apache License, Version 2.0
006     (the "License"); you may not use this file except in compliance with
007     the License.  You may obtain a copy of the License at
008    
009          http://www.apache.org/licenses/LICENSE-2.0
010    
011     Unless required by applicable law or agreed to in writing, software
012     distributed under the License is distributed on an "AS IS" BASIS,
013     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     See the License for the specific language governing permissions and
015     limitations under the License.
016    */
017    package org.apache.commons.inject.impl;
018    
019    import java.lang.annotation.Annotation;
020    import java.lang.reflect.Array;
021    import java.lang.reflect.Constructor;
022    import java.lang.reflect.Field;
023    import java.lang.reflect.Method;
024    import java.lang.reflect.Modifier;
025    import java.lang.reflect.ParameterizedType;
026    import java.lang.reflect.Type;
027    import java.util.ArrayList;
028    import java.util.List;
029    
030    import javax.inject.Inject;
031    import javax.inject.Named;
032    import javax.inject.Provider;
033    
034    import org.apache.commons.inject.api.IBinding;
035    import org.apache.commons.inject.api.IInjector;
036    import org.apache.commons.inject.api.IKey;
037    import org.apache.commons.inject.api.IPoint;
038    import org.apache.commons.inject.api.IProvider;
039    import org.apache.commons.inject.api.Key;
040    import org.apache.commons.inject.impl.bind.DefaultBinding;
041    import org.apache.commons.inject.util.Generics;
042    
043    public class Introspector {
044            private static final Introspector introspector = new Introspector();
045            private static final Class<Object> PROVIDER_CLASS = Generics.cast(Provider.class);
046    
047            // Private constructor, to avoid accidental instantiation.
048            private Introspector() {
049            }
050    
051            public static Introspector getInstance() {
052                    return introspector;
053            }
054    
055            public <T> AbstractBaseProvider<T> getProvider(Class<T> pType, IPoint<T> pPoint, IMutableBindingSource pBindings) {
056                    @SuppressWarnings("unchecked")
057                    final Constructor<T>[] constructors = (Constructor<T>[]) pType.getDeclaredConstructors();
058                    for (Constructor<T> constructor : constructors) {
059                            if (constructor.isAnnotationPresent(Inject.class)) {
060                                    return getProvider(constructor, pBindings);
061                            }
062                    }
063                    return new DefaultProvider<T>(pType, pPoint);
064            }
065    
066            public <T> AbstractBaseProvider<T> getProvider(Constructor<? extends T> pConstructor, IMutableBindingSource pBindings) {
067                    @SuppressWarnings("unchecked")
068                    final Class<Object>[] parameterClasses = (Class<Object>[]) pConstructor.getParameterTypes();
069                    final Type[] parameterTypes = pConstructor.getGenericParameterTypes();
070                    final Annotation[][] parameterAnnotations = pConstructor.getParameterAnnotations();
071                    final IBinding<Object>[] parameterBindings = getBindings(parameterClasses, parameterTypes,
072                                    parameterAnnotations, pBindings,
073                                    "Required to invoke the constructor " + pConstructor);
074                    @SuppressWarnings("unchecked")
075                    final Constructor<T> constructor = (Constructor<T>) pConstructor;
076                    return new FactoryMethodProvider<T>(constructor,
077                                                                                            getPoint(constructor.getDeclaringClass(), pBindings),
078                                                                                            parameterBindings);
079            }
080    
081            public <T> AbstractBaseProvider<T> getProvider(Method pMethod, IMutableBindingSource pBindings) {
082                    @SuppressWarnings("unchecked")
083                    final Class<Object>[] parameterClasses = (Class<Object>[]) pMethod.getParameterTypes();
084                    final Type[] parameterTypes = pMethod.getGenericParameterTypes();
085                    final Annotation[][] parameterAnnotations = pMethod.getParameterAnnotations();
086                    final IBinding<Object>[] parameterBindings = getBindings(parameterClasses, parameterTypes, parameterAnnotations, pBindings,
087                            "Required to invoke the method " + pMethod);
088                    @SuppressWarnings("unchecked")
089                    final Class<T> cl = (Class<T>) pMethod.getReturnType();
090                    final IPoint<T> point = getPoint(cl, pBindings);
091                    return new FactoryMethodProvider<T>(pMethod, parameterBindings, point);
092            }
093    
094            private IBinding<Object>[] getBindings(Class<Object>[] pParameterClasses, Type[] pParameterTypes, Annotation[][] pParameterAnnotations, IMutableBindingSource pBindings,
095                            String pCause) {
096                    @SuppressWarnings("unchecked")
097                    final IBinding<Object>[] bindings = (IBinding<Object>[]) Array.newInstance(IBinding.class, pParameterTypes.length);
098                    for (int i = 0;  i < bindings.length;  i++) {
099                            final Class<Object> cl = pParameterClasses[i];
100                            final Annotation[] annotations = pParameterAnnotations[i];
101                            bindings[i] = getBinding(cl, pParameterTypes[i], annotations, pBindings, pCause);
102                    }
103                    return bindings;
104            }
105    
106            private IBinding<Object> getBinding(Class<Object> pClass, Type pType, Annotation[] pAnnotations, IMutableBindingSource pBindings, String pCause) {
107                    String name = Key.NO_NAME;
108                    for (Annotation annotation : pAnnotations) {
109                            if (annotation instanceof Named) {
110                                    name = ((Named) annotation).value();
111                                    break;
112                            }
113                    }
114                    if (pClass == PROVIDER_CLASS  &&  pType != null  &&  pType instanceof ParameterizedType) {
115                            final ParameterizedType ptype = (ParameterizedType) pType;
116                            final Type[] typeArgs = ptype.getActualTypeArguments();
117                            if (typeArgs != null  &&  typeArgs.length == 1) {
118                                    final Type typeArg = typeArgs[0];
119                                    if (typeArg instanceof Class<?>) {
120                                            @SuppressWarnings("unchecked")
121                                            final Class<Object> cl = (Class<Object>) typeArg;
122                                            final IKey<Object> key = new Key<Object>(cl, name);
123                                            final IBinding<Object> binding1 = pBindings.requireBinding(key, pCause);
124                                            final IProvider<Object> provider = new IProvider<Object>(){
125                                                    @Override
126                                                    public Object get() {
127                                                            return new Provider<Object>(){
128                                                                    @Override
129                                                                    public Object get() {
130                                                                            return binding1.getProvider().get();
131                                                                    }
132                                                            };
133                                                    }
134    
135                                                    @Override
136                                                    public Class<? extends Object> getType() {
137                                                            return Provider.class;
138                                                    }
139    
140                                                    @Override
141                                                    public Object get(IInjector pInjector) {
142                                                            return get();
143                                                    }
144                                            };
145                                            final IPoint<Object> point = new IPoint<Object>(){
146                                                    @Override
147                                                    public void injectTo(Object pInstance,
148                                                                    IInjector pInjector) {
149                                                            // Does nothing.
150                                                    }
151                                            };
152                                            final IBinding<Object> binding2 = new DefaultBinding<Object>(provider, point);
153                                            return binding2;
154                                    }
155                            }
156                    }
157                    final IKey<Object> key = new Key<Object>(pClass, name);
158                    return pBindings.requireBinding(key, pCause);
159            }
160    
161            public <T> ListPoint<T> getPoint(Class<T> pType, IMutableBindingSource pBindings) {
162                    final List<IPoint<T>> points = new ArrayList<IPoint<T>>();
163                    final Field[] fields = pType.getDeclaredFields();
164                    for (final Field f : fields) {
165                            if (Modifier.isStatic(f.getModifiers())) {
166                                    continue;
167                            }
168                            if (!f.isAnnotationPresent(Inject.class)) {
169                                    continue;
170                            }
171                            @SuppressWarnings("unchecked")
172                            Class<Object> type = (Class<Object>) f.getType();
173                            Type genericType = f.getGenericType();
174                            IBinding<Object> binding = null;
175                            Annotation[] annotations = f.getAnnotations();
176                            binding = getBinding(type, genericType, annotations, pBindings,
177                                            "Required to inject to an instance of " + pType.getName());
178                            final IPoint<T> point = new FieldPoint<T>(binding, f);
179                            points.add(point);
180                    }
181                    final Method[] methods = pType.getDeclaredMethods();
182                    for (final Method m : methods) {
183                            if (Modifier.isStatic(m.getModifiers())) {
184                                    continue;
185                            }
186                            if (!m.isAnnotationPresent(Inject.class)) {
187                                    continue;
188                            }
189                            @SuppressWarnings("unchecked")
190                            Class<Object>[] parameterClasses = (Class<Object>[]) m.getParameterTypes();
191                            Type[] parameterTypes = m.getGenericParameterTypes();
192                            Annotation[][] annotations = m.getParameterAnnotations();
193                            final IBinding<Object>[] bindings = getBindings(parameterClasses, parameterTypes, annotations, pBindings,
194                                            "Required to inject to an instance of " + pType.getName());
195                            final IPoint<T> point = new MethodPoint<T>(bindings, m);
196                            points.add(point);
197                    }
198                    return new ListPoint<T>(points);
199            }
200    }