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 }