blob: a6d021b03bc4dd2b51648f23c94e44b9980775b7 [file] [log] [blame]
crazyboblee81303cd2007-05-09 00:44:37 +00001/**
2 * Copyright (C) 2007 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject;
18
limpbizkit916f5482008-04-16 20:51:14 +000019import com.google.inject.internal.ErrorMessages;
limpbizkitf530b252008-05-27 23:03:42 +000020import com.google.inject.internal.StackTraceElements;
crazyboblee81303cd2007-05-09 00:44:37 +000021import com.google.inject.spi.SourceProvider;
22import com.google.inject.spi.SourceProviders;
limpbizkitf530b252008-05-27 23:03:42 +000023
kevinb9ncad2c2b2007-05-15 17:28:03 +000024import java.lang.annotation.Annotation;
25import java.lang.reflect.InvocationTargetException;
crazyboblee81303cd2007-05-09 00:44:37 +000026import java.lang.reflect.Method;
27import java.lang.reflect.Type;
crazyboblee81303cd2007-05-09 00:44:37 +000028import java.util.ArrayList;
kevinb9ncad2c2b2007-05-15 17:28:03 +000029import java.util.List;
crazyboblee81303cd2007-05-09 00:44:37 +000030
31/**
limpbizkitf530b252008-05-27 23:03:42 +000032 * Creates bindings to methods annotated with {@literal @}
crazybobleed0c4b8b2007-09-06 02:47:04 +000033 * {@link com.google.inject.Provides}. Use the scope and binding annotations
34 * on the provider method to configure the binding.
crazyboblee81303cd2007-05-09 00:44:37 +000035 */
36public class ProviderMethods {
37
38 /**
39 * Returns a module which creates bindings for provider methods from the
40 * given object.
41 */
42 public static Module from(Object providers) {
43 return new ProviderMethodsModule(providers);
44 }
45
46 static class ProviderMethodsModule extends AbstractModule {
47
48 final Object providers;
49
50 Object source;
51
52 final SourceProvider sourceProvider = new SourceProvider() {
53 public Object source() {
54 return source;
55 }
56 };
57
58 ProviderMethodsModule(Object providers) {
59 this.providers = providers;
60 }
61
62 protected void configure() {
63 SourceProviders.withDefault(sourceProvider, new Runnable() {
64 public void run() {
65 bindProviderMethods(providers.getClass());
66 }
67 });
68 }
69
70 void bindProviderMethods(Class<?> clazz) {
71 if (clazz == Object.class) {
72 return;
73 }
74
75 bindProviderMethods(clazz.getSuperclass());
76
77 for (Method method : clazz.getDeclaredMethods()) {
78 if (method.isAnnotationPresent(Provides.class)) {
79 bindProviderMethod(method);
80 }
81 }
82 }
83
crazybobleefb6285a2007-09-09 20:21:53 +000084 <T> void bindProviderMethod(final Method method) {
crazyboblee81303cd2007-05-09 00:44:37 +000085 this.source = StackTraceElements.forMember(method);
86
87 method.setAccessible(true);
88
89 Class<? extends Annotation> scopeAnnotation
90 = findScopeAnnotation(method.getAnnotations());
91 Annotation bindingAnnotation = findBindingAnnotation(
92 method.getAnnotations());
93
94 final List<Provider<?>> parameterProviders
95 = findParameterProviders(method);
96
crazybobleefb6285a2007-09-09 20:21:53 +000097 // Define T as the method's return type.
98 @SuppressWarnings("unchecked")
99 TypeLiteral<T> returnType
100 = (TypeLiteral<T>) TypeLiteral.get(method.getGenericReturnType());
101
102 Provider<T> provider = new Provider<T>() {
103 public T get() {
crazyboblee81303cd2007-05-09 00:44:37 +0000104 Object[] parameters = new Object[parameterProviders.size()];
105 for (int i = 0; i < parameters.length; i++) {
106 parameters[i] = parameterProviders.get(i).get();
107 }
108
109 try {
crazybobleefb6285a2007-09-09 20:21:53 +0000110 // We know this cast is safe becase T is the method's return type.
111 @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
112 T result = (T) method.invoke(providers, parameters);
113 return result;
crazyboblee81303cd2007-05-09 00:44:37 +0000114 }
115 catch (IllegalAccessException e) {
116 throw new AssertionError(e);
117 }
118 catch (InvocationTargetException e) {
119 throw new RuntimeException(e);
120 }
121 }
122 };
123
crazyboblee81303cd2007-05-09 00:44:37 +0000124 if (scopeAnnotation == null && bindingAnnotation == null) {
crazybobleefb6285a2007-09-09 20:21:53 +0000125 bind(returnType).toProvider(provider);
crazyboblee81303cd2007-05-09 00:44:37 +0000126 } else if (scopeAnnotation == null) {
crazybobleefb6285a2007-09-09 20:21:53 +0000127 bind(returnType).annotatedWith(bindingAnnotation).toProvider(provider);
crazyboblee81303cd2007-05-09 00:44:37 +0000128 } else if (bindingAnnotation == null) {
crazybobleefb6285a2007-09-09 20:21:53 +0000129 bind(returnType).toProvider(provider).in(scopeAnnotation);
crazyboblee81303cd2007-05-09 00:44:37 +0000130 } else {
crazybobleefb6285a2007-09-09 20:21:53 +0000131 bind(returnType)
crazyboblee81303cd2007-05-09 00:44:37 +0000132 .annotatedWith(bindingAnnotation)
133 .toProvider(provider)
134 .in(scopeAnnotation);
135 }
136 }
137
138 List<Provider<?>> findParameterProviders(Method method) {
139 List<Provider<?>> parameterProviders = new ArrayList<Provider<?>>();
140
141 Type[] parameterTypes = method.getGenericParameterTypes();
142 Annotation[][] parameterAnnotations = method.getParameterAnnotations();
143 for (int i = 0; i < parameterTypes.length; i++) {
144 Annotation bindingAnnotation =
145 findBindingAnnotation(parameterAnnotations[i]);
146 Key<?> key = bindingAnnotation == null
147 ? Key.get(parameterTypes[i])
148 : Key.get(parameterTypes[i], bindingAnnotation);
149 Provider<?> provider = getProvider(key);
150 parameterProviders.add(provider);
151 }
152
153 return parameterProviders;
154 }
155
156 Class<? extends Annotation> findScopeAnnotation(Annotation[] annotations) {
157 Class<? extends Annotation> found = null;
158
159 for (Annotation annotation : annotations) {
160 if (annotation.annotationType()
161 .isAnnotationPresent(ScopeAnnotation.class)) {
162 if (found != null) {
163 addError(ErrorMessages.DUPLICATE_SCOPE_ANNOTATIONS,
164 "@" + found.getSimpleName(),
165 "@" + annotation.annotationType().getSimpleName()
166 );
167 } else {
168 found = annotation.annotationType();
169 }
170 }
171 }
172
173 return found;
174 }
175
176 Annotation findBindingAnnotation(Annotation[] annotations) {
177 Annotation found = null;
178
179 for (Annotation annotation : annotations) {
180 if (annotation.annotationType()
181 .isAnnotationPresent(BindingAnnotation.class)) {
182 if (found != null) {
183 addError(ErrorMessages.DUPLICATE_BINDING_ANNOTATIONS,
184 "@" + found.annotationType().getSimpleName(),
185 "@" + annotation.annotationType().getSimpleName()
186 );
187 } else {
188 found = annotation;
189 }
190 }
191 }
192
193 return found;
194 }
195 }
196}