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.bind;
018    
019    import java.lang.annotation.Annotation;
020    import java.lang.reflect.Constructor;
021    import java.lang.reflect.Method;
022    import java.lang.reflect.Modifier;
023    import java.util.List;
024    
025    import org.apache.commons.inject.api.IBinding;
026    import org.apache.commons.inject.api.IInjector;
027    import org.apache.commons.inject.api.IKey;
028    import org.apache.commons.inject.api.IPoint;
029    import org.apache.commons.inject.api.IProvider;
030    import org.apache.commons.inject.api.Key;
031    import org.apache.commons.inject.api.bind.IAnnotatedBindingBuilder;
032    import org.apache.commons.inject.api.bind.ILinkedBindingBuilder;
033    import org.apache.commons.inject.api.bind.IScopedBindingBuilder;
034    import org.apache.commons.inject.api.bind.Scopes;
035    import org.apache.commons.inject.api.bind.IBinder.IInjectionListener;
036    import org.apache.commons.inject.api.bind.IBinder.IInjectionParticipator;
037    import org.apache.commons.inject.impl.AbstractBaseProvider;
038    import org.apache.commons.inject.impl.AbstractScopedProvider;
039    import org.apache.commons.inject.impl.Introspector;
040    import org.apache.commons.inject.impl.ListPoint;
041    import org.apache.commons.inject.impl.MutableBindingSet;
042    import org.apache.commons.inject.impl.AbstractBindingSet.MappedKey;
043    
044    import com.google.inject.Provider;
045    
046    
047    /**
048     * Default implementation of a binding builder. Implements
049     * {@link IAnnotatedBindingBuilder}, thus {@link IScopedBindingBuilder},
050     * and {@link ILinkedBindingBuilder} as well. In other words: Under the
051     * hood, you are always using this one and only binding builder.
052     */
053    public class DefaultBindingBuilder<T> implements IAnnotatedBindingBuilder<T> {
054            private final Class<T> sourceType;
055            private final IKey<T> sourceKey;
056            private Annotation sourceAnnotation;
057            private Class<? extends Annotation> sourceAnnotationType;
058            private T targetInstance;
059            private Class<? extends T> targetType;
060            private Constructor<? extends T> targetConstructor;
061            private Method targetMethod;
062            private Provider<? extends T> targetProvider;
063            private IProvider<? extends T> targetIProvider;
064            private Scopes scope;
065    
066            public DefaultBindingBuilder(Class<T> pType) {
067                    this(pType, Key.NO_NAME);
068            }
069    
070            public DefaultBindingBuilder(Class<T> pType, String pName) {
071                    sourceKey = new Key<T>(pType, pName);
072                    sourceType = pType;
073            }
074    
075            public DefaultBindingBuilder(IKey<T> pKey) {
076                    sourceKey = pKey;
077                    sourceType = null;
078            }
079            
080            @Override
081            public void toInstance(T pInstance) {
082                    if (pInstance == null) {
083                            throw new NullPointerException("The target instance must not be null.");
084                    }
085                    checkNoTarget();
086                    targetInstance = pInstance;
087                    asEagerSingleton();
088            }
089    
090            private void checkNoTarget() {
091                    if (targetInstance != null
092                            ||  targetType != null
093                            ||  targetConstructor != null
094                            ||  targetMethod != null
095                            ||  targetProvider != null
096                            ||  targetIProvider != null) {
097                            throw new IllegalStateException("The methods " + TARGET_METHOD_LIST
098                                            + " are mutually exclusive, and may be invoked only once.");
099                    }
100            }
101    
102            private static final String SCOPE_METHOD_LIST
103                    = "toInstance(Object), scope(Scopes), asEagerSingleton(), and asLazySingleton()";
104            private static final String TARGET_METHOD_LIST
105                    = "toInstance(Object), to(Class), to(Constructor), to(Method),"
106                    + " to(Provider, Class), to(IProvider)";
107            
108            @Override
109            public IScopedBindingBuilder<T> to(Class<? extends T> pImplClass) {
110                    if (pImplClass == null) {
111                            throw new NullPointerException("The target class must not be null.");
112                    }
113                    checkNoTarget();
114                    targetType = pImplClass;
115                    return this;
116            }
117    
118            @Override
119            public IScopedBindingBuilder<T> to(Constructor<? extends T> pConstructor) {
120                    if (pConstructor == null) {
121                            throw new NullPointerException("The target constructor must not be null.");
122                    }
123                    checkNoTarget();
124                    targetConstructor = pConstructor;
125                    return this;
126            }
127    
128            @Override
129            public IScopedBindingBuilder<T> to(Method pFactoryMethod) {
130                    if (pFactoryMethod == null) {
131                            throw new NullPointerException("The target constructor must not be null.");
132                    }
133                    if (!Modifier.isStatic(pFactoryMethod.getModifiers())) {
134                            throw new IllegalStateException("The target method must be static.");
135                    }
136                    if (pFactoryMethod.getReturnType().isPrimitive()) {
137                            throw new IllegalStateException("The target method must return a non-primitive result.");
138                    }
139                    if (pFactoryMethod.getReturnType().isArray()) {
140                            throw new IllegalStateException("The target method must return a single object, and not an array.");
141                    }
142                    if (Void.TYPE == pFactoryMethod.getReturnType()) {
143                            throw new IllegalStateException("The target method must return a non-void result.");
144                    }
145                    checkNoTarget();
146                    targetMethod = pFactoryMethod;
147                    return this;
148            }
149    
150            @Override
151            public <S extends T> IScopedBindingBuilder<T> to(Class<S> pType,
152                            Provider<S> pProvider) {
153                    if (pType == null) {
154                            throw new NullPointerException("The target type must not be null.");
155                    }
156                    if (pProvider == null) {
157                            throw new NullPointerException("The target provider must not be null.");
158                    }
159                    checkNoTarget();
160                    targetType = pType;
161                    targetProvider = pProvider;
162                    return this;
163            }
164    
165            @Override
166            public IScopedBindingBuilder<T> to(IProvider<? extends T> pProvider) {
167                    if (pProvider == null) {
168                            throw new NullPointerException("The target provider must not be null.");
169                    }
170                    checkNoTarget();
171                    targetIProvider = pProvider;
172                    return this;
173            }
174    
175            @Override
176            public void scope(Scopes pScope) {
177                    if (pScope == null) {
178                            throw new NullPointerException("The target scope must not be null.");
179                    }
180                    if (scope != null) {
181                            throw new IllegalStateException("The methods " + SCOPE_METHOD_LIST
182                                            + " are mutually exclusive, and may be invoked only once.");
183                    }
184                    scope = pScope;
185            }
186    
187            @Override
188            public void asEagerSingleton() {
189                    scope(Scopes.EAGER_SINGLETON);
190            }
191    
192            @Override
193            public void asLazySingleton() {
194                    scope(Scopes.LAZY_SINGLETON);
195            }
196    
197            @Override
198            public ILinkedBindingBuilder<T> annotatedWith(Annotation pAnnotation) {
199                    if (pAnnotation == null) {
200                            throw new NullPointerException("The annotation must not be null.");
201                    }
202                    if (sourceAnnotation != null) {
203                            throw new IllegalStateException("The method annotatedWith(Annotation) must not be invoked twice.");
204                    }
205                    sourceAnnotation = pAnnotation;
206                    return this;
207            }
208    
209            @Override
210            public ILinkedBindingBuilder<T> annotatedWith(
211                            Class<? extends Annotation> pAnnotationType) {
212                    if (pAnnotationType == null) {
213                            throw new NullPointerException("The annotation type must not be null.");
214                    }
215                    if (sourceAnnotationType != null) {
216                            throw new IllegalStateException("The method annotatedWith(Class) must not be invoked twice.");
217                    }
218                    sourceAnnotationType = pAnnotationType;
219                    return this;
220            }
221    
222            public void build(MutableBindingSet pBindings, final List<IInjectionListener> pListeners,
223                            final List<IInjectionParticipator> pParticipators) {
224                    final Class<T> baseType = getBaseType();
225                    ListPoint<T> point = Introspector.getInstance().getPoint(baseType, pBindings);
226                    final IKey<T> key = sourceKey;
227                    if (pParticipators != null) {
228                            for (IInjectionParticipator participator : pParticipators) {
229                                    final List<IPoint<Object>> points = participator.getPoints(key, baseType);
230                                    if (points != null) {
231                                            for (IPoint<Object> p : points) {
232                                                    @SuppressWarnings("unchecked")
233                                                    final IPoint<T> pnt = (IPoint<T>) p;
234                                                    point.add(pnt);
235                                            }
236                                    }
237                            }
238                    }
239                    if (pListeners != null  &&  !pListeners.isEmpty()) {
240                            point.add(new IPoint<T>(){
241                                    @Override
242                                    public void injectTo(T pInstance, IInjector pInjector) {
243                                            for (IInjectionListener listener : pListeners) {
244                                                    listener.initialized(key, pInstance);
245                                            }
246                                    }
247                            });
248                    }
249                    final IProvider<T> baseProvider = getBaseProvider(baseType, point, pBindings);
250                    final IProvider<T> scopedProvider = getScopedProvider(baseProvider);
251                    final IBinding<T> binding = new DefaultBinding<T>(scopedProvider, point);
252                    final Annotation[] annotations;
253                    if (sourceAnnotation == null) {
254                            annotations = Key.NO_ANNOTATIONS;
255                    } else {
256                            annotations = new Annotation[]{ sourceAnnotation };
257                    }
258                    final MappedKey<T> mkey = new MappedKey<T>(sourceKey.getType(), sourceKey.getName(),
259                                    annotations, sourceAnnotationType);
260                                    
261                    pBindings.add(mkey, binding);
262            }
263    
264            private Class<T> getBaseType() {
265                    if (targetInstance != null) {
266                            @SuppressWarnings("unchecked")
267                            final Class<T> cl = (Class<T>) targetInstance.getClass();
268                            return cl;
269                    }
270                    if (targetProvider != null) {
271                            @SuppressWarnings("unchecked")
272                            final Class<T> cl = (Class<T>) targetType;
273                            return cl;
274                    }
275                    if (targetIProvider != null) {
276                            @SuppressWarnings("unchecked")
277                            final Class<T> cl = (Class<T>) targetIProvider.getType();
278                            return cl;
279                    }
280                    if (targetType != null) {
281                            @SuppressWarnings("unchecked")
282                            final Class<T> cl = (Class<T>) targetType;
283                            return cl;
284                    }
285                    if (targetConstructor != null) {
286                            @SuppressWarnings("unchecked")
287                            final Class<T> cl = (Class<T>) targetConstructor.getDeclaringClass();
288                            return cl;
289                    }
290                    if (targetMethod != null) {
291                            @SuppressWarnings("unchecked")
292                            final Class<T> cl = (Class<T>) targetMethod.getReturnType();
293                            return cl;
294                    }
295                    if (sourceType == null) {
296                            throw new IllegalStateException("Neither of the methods "
297                                            + TARGET_METHOD_LIST + " has been invoked on this binding builder,"
298                                            + " which is required when binding a key.");
299                    }
300                    if (sourceType.isInterface()  ||  Modifier.isAbstract(sourceType.getModifiers())) {
301                            throw new IllegalStateException("Neither of the methods "
302                                            + TARGET_METHOD_LIST + " has been invoked on this binding builder, "
303                                            + " but cannot bind " + sourceType.getName()
304                                            + " as target type, because it is an interface, or an abstract class.");
305                    }
306                    return sourceType;
307            }
308            private AbstractBaseProvider<T> getBaseProvider(Class<T> pType, IPoint<T> pPoint, MutableBindingSet pBindings) {
309                    if (targetInstance != null) {
310                            return new AbstractBaseProvider<T>(pType, pPoint){
311                                    @Override
312                                    public T get() {
313                                            return targetInstance;
314                                    }
315                            };
316                    }
317                    if (targetProvider != null) {
318                            return new AbstractBaseProvider<T>(pType, pPoint){
319                                    @Override
320                                    public T get() {
321                                            return (T) targetProvider.get();
322                                    }
323                            };
324                    }
325                    if (targetIProvider != null) {
326                            return new AbstractBaseProvider<T>(pType, pPoint){
327                                    @Override
328                                    public T get() {
329                                            return (T) targetIProvider.get();
330                                    }
331                                    
332                            };
333                    }
334                    if (targetType != null) {
335                            @SuppressWarnings("unchecked")
336                            final Class<T> cl = (Class<T>) targetType;
337                            final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(cl, pPoint, pBindings);
338                            return abp;
339                    }
340                    if (targetConstructor != null) {
341                            final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(targetConstructor, pBindings);
342                            return abp;
343                    }
344                    if (targetMethod != null) {
345                            @SuppressWarnings("unchecked")
346                            final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(targetMethod, pBindings);
347                            return abp;
348                    }
349                    if (sourceType != null) {
350                            final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(sourceType, pPoint, pBindings);
351                            return abp;
352                    }
353                    throw new IllegalStateException("Neither of the methods "
354                                    + TARGET_METHOD_LIST + " has been invoked on this binding builder.");
355            }
356    
357            public AbstractScopedProvider<T> getScopedProvider(IProvider<T> pBaseProvider) {
358                    if (scope == null) {
359                            throw new IllegalStateException("Neither of the methods "
360                                            + SCOPE_METHOD_LIST + " has been invoked on this binding builder.");
361                            
362                    }
363                    switch(scope) {
364                    case PER_CALL:
365                            return new PerCallProvider<T>(pBaseProvider);
366                    case EAGER_SINGLETON:
367                            return new EagerSingletonProvider<T>(pBaseProvider);
368                    case LAZY_SINGLETON:
369                            return new LazySingletonProvider<T>(pBaseProvider);
370                    default:
371                            throw new IllegalStateException("Invalid scope: " + scope);
372                    }
373            }
374    }