blob: 458e1740a510f219dfc8653c5950a04269d5ee05 [file] [log] [blame]
limpbizkit3d58d6b2008-03-08 16:11:47 +00001/**
2 * Copyright (C) 2008 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
19import com.google.inject.commands.BindCommand;
20import com.google.inject.commands.BindConstantCommand;
21import com.google.inject.commands.BindScoping;
22import com.google.inject.commands.BindTarget;
23import com.google.inject.internal.Annotations;
24import com.google.inject.internal.Objects;
25import com.google.inject.internal.StackTraceElements;
26
27import java.lang.annotation.Annotation;
28import java.lang.reflect.Type;
29import java.util.*;
30import java.util.logging.Logger;
31
32/**
33 * Handles {@link Binder#bind} and {@link Binder#bindConstant} commands.
34 *
35 * @author crazybob@google.com (Bob Lee)
36 * @author jessewilson@google.com (Jesse Wilson)
37 */
38class BindCommandProcessor extends CommandProcessor {
39
40 private final InjectorImpl injector;
41 private final Map<Class<? extends Annotation>, Scope> scopes;
42 private final List<CreationListener> creationListeners
43 = new ArrayList<CreationListener>();
44 private final List<ContextualCallable<Void>> preloaders
45 = new ArrayList<ContextualCallable<Void>>();
46 private final Stage stage;
47 private final Map<Key<?>, BindingImpl<?>> bindings;
48 private final List<MembersInjector> membersInjectors = new ArrayList<MembersInjector>();
49
50 BindCommandProcessor(InjectorImpl injector,
51 Map<Class<? extends Annotation>, Scope> scopes,
52 Stage stage,
53 Map<Key<?>, BindingImpl<?>> bindings) {
54 this.injector = injector;
55 this.scopes = scopes;
56 this.stage = stage;
57 this.bindings = bindings;
58 }
59
60 @Override public <T> Boolean visitBind(BindCommand<T> command) {
61 final Object source = command.getSource();
62
63 final Key<T> key = command.getKey();
64 Class<? super T> rawType = key.getTypeLiteral().getRawType();
65
66 if (rawType == Provider.class) {
67 addError(source, ErrorMessages.BINDING_TO_PROVIDER);
68 return true;
69 }
70
71 if (Logger.class == rawType) {
72 // TODO(jessewilson): assert this is coming from the internal module?
73 // addError(source, ErrorMessages.LOGGER_ALREADY_BOUND);
74 // return true;
75 }
76
77 validateKey(command.getSource(), command.getKey());
78
79 // TODO(jessewilson): Scope annotation on type, like @Singleton
80 final boolean shouldPreload = command.getScoping().isEagerSingleton();
81 final Scope scope = command.getScoping().acceptVisitor(new BindScoping.Visitor<Scope>() {
82 public Scope visitEagerSingleton() {
83 return Scopes.SINGLETON;
84 }
85
86 public Scope visitScope(Scope scope) {
87 return scope;
88 }
89
90 public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
91 Scope scope = scopes.get(scopeAnnotation);
92 if (scope != null) {
93 return scope;
94 } else {
95 addError(source, ErrorMessages.SCOPE_NOT_FOUND,
96 "@" + scopeAnnotation.getSimpleName());
97 return Scopes.NO_SCOPE;
98 }
99 }
100
101 public Scope visitNoScoping() {
102 return null;
103 }
104 });
105
106 command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
107 public Void visitToInstance(T instance) {
108 InternalFactory<? extends T> factory = new ConstantFactory<T>(instance);
109 registerInstanceForInjection(instance);
110 InternalFactory<? extends T> scopedFactory
111 = Scopes.scope(key, injector, factory, scope);
112 createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
113 injector, key, source, scopedFactory, instance));
114 return null;
115 }
116
117 public Void visitToProvider(Provider<? extends T> provider) {
118 InternalFactory<? extends T> factory
119 = new InternalFactoryToProviderAdapter<T>(provider, source);
120 registerInstanceForInjection(provider);
121 InternalFactory<? extends T> scopedFactory
122 = Scopes.scope(key, injector, factory, scope);
123 createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
124 injector, key, source, scopedFactory, scope, provider));
125 return null;
126 }
127
128 public Void visitToProviderKey(Key<? extends Provider<? extends T>> providerKey) {
129 final BoundProviderFactory<T> boundProviderFactory =
130 new BoundProviderFactory<T>(providerKey, source);
131 creationListeners.add(boundProviderFactory);
132 InternalFactory<? extends T> scopedFactory = Scopes.scope(
133 key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
134 createBinding(source, shouldPreload, new LinkedProviderBindingImpl<T>(
135 injector, key, source, scopedFactory, scope, providerKey));
136 return null;
137 }
138
139 public Void visitToKey(Key<? extends T> targetKey) {
140 if (key.equals(targetKey)) {
141 addError(source, ErrorMessages.RECURSIVE_BINDING);
142 }
143
144 FactoryProxy<T> factory = new FactoryProxy<T>(key, targetKey, source);
145 creationListeners.add(factory);
146 InternalFactory<? extends T> scopedFactory
147 = Scopes.scope(key, injector, factory, scope);
148 createBinding(source, shouldPreload, new LinkedBindingImpl<T>(
149 injector, key, source, scopedFactory, scope, targetKey));
150 return null;
151 }
152
153 public Void visitUntargetted() {
154 Type type = key.getTypeLiteral().getType();
155
156 // Error: Missing implementation.
157 // Example: bind(Date.class).annotatedWith(Red.class);
158 // We can't assume abstract types aren't injectable. They may have an
159 // @ImplementedBy annotation or something.
160 if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
161 addError(source, ErrorMessages.MISSING_IMPLEMENTATION);
162 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
163 return null;
164 }
165
166 // This cast is safe after the preceeding check.
167 @SuppressWarnings("unchecked")
168 Class<T> clazz = (Class<T>) type;
169
170 BindingImpl<T> binding = injector.createBindingFromType(clazz, scope, source);
171 // TODO: Should we clean up the binding left behind in jitBindings?
172
173 if (binding == null) {
174 addError(source, ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, clazz);
175 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
176 return null;
177 }
178
179 createBinding(source, shouldPreload, binding);
180 return null;
181 }
182 });
183
184 return true;
185 }
186
187 private <T> void validateKey(Object source, Key<T> key) {
188 if (key.hasAnnotationType()) {
189 Class<? extends Annotation> annotationType = key.getAnnotationType();
190
191 if (!Annotations.isRetainedAtRuntime(annotationType)) {
192 addError(StackTraceElements.forType(annotationType),
193 ErrorMessages.MISSING_RUNTIME_RETENTION, source);
194 }
195
196 if (!Key.isBindingAnnotation(annotationType)) {
197 addError(StackTraceElements.forType(annotationType),
198 ErrorMessages.MISSING_BINDING_ANNOTATION, source);
199 }
200 }
201 }
202
203 <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
204 return new InvalidBindingImpl<T>(injector, key, source);
205 }
206
207 void registerInstanceForInjection(final Object o) {
208 membersInjectors.add(new MembersInjector(o));
209 }
210
211 public void validate(InjectorImpl injector) {
212 for (MembersInjector membersInjector : membersInjectors) {
213 membersInjector.checkDependencies(injector);
214 }
215 }
216
217 public void injectMembers(InjectorImpl injector) {
218 for (MembersInjector membersInjector : membersInjectors) {
219 membersInjector.injectMembers(injector);
220 }
221 }
222
223 private static class MembersInjector {
224 final Object o;
225
226 MembersInjector(Object o) {
227 this.o = o;
228 }
229
230 void checkDependencies(InjectorImpl injector) {
231 injector.injectors.get(o.getClass());
232 }
233
234 void injectMembers(InjectorImpl injector) {
235 injector.injectMembers(o);
236 }
237 }
238
239 @Override public Boolean visitBindConstant(BindConstantCommand command) {
240 Object value = command.getTarget().get();
241 if (value == null) {
242 addError(command.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
243 }
244
245 validateKey(command.getSource(), command.getKey());
246 ConstantFactory<Object> factory = new ConstantFactory<Object>(value);
247 putBinding(new ContantBindingImpl<Object>(
248 injector, command.getKey(), command.getSource(), factory, value));
249
250 return true;
251 }
252
253 private <T> void createBinding(Object source, boolean shouldPreload,
254 BindingImpl<T> binding) {
255 putBinding(binding);
256
257 // Register to preload if necessary.
258 if (binding.getScope() == Scopes.SINGLETON) {
259 if (stage == Stage.PRODUCTION || shouldPreload) {
260 preloaders.add(new BindingPreloader(binding.key, binding.internalFactory));
261 }
262 } else {
263 if (shouldPreload) {
264 addError(source, ErrorMessages.PRELOAD_NOT_ALLOWED);
265 }
266 }
267 }
268
269 public void runPreloaders(InjectorImpl injector) {
270 injector.callInContext(new ContextualCallable<Void>() {
271 public Void call(InternalContext context) {
272 for (ContextualCallable<Void> preloader : preloaders) {
273 preloader.call(context);
274 }
275 return null;
276 }
277 });
278 }
279
280 public void runCreationListeners(InjectorImpl injector) {
281 for (CreationListener creationListener : creationListeners) {
282 creationListener.notify(injector);
283 }
284 }
285
286 private static class BindingPreloader implements ContextualCallable<Void> {
287 private final Key<?> key;
288 private final InternalFactory<?> factory;
289
290 public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
291 this.key = key;
292 this.factory = Objects.nonNull(factory, "factory");
293 }
294
295 public Void call(InternalContext context) {
296 InjectionPoint<?> injectionPoint
297 = InjectionPoint.newInstance(key, context.getInjectorImpl());
298 context.setInjectionPoint(injectionPoint);
299 try {
300 factory.get(context, injectionPoint);
301 return null;
302 } catch(ProvisionException provisionException) {
303 provisionException.addContext(injectionPoint);
304 throw provisionException;
305 } finally {
306 context.setInjectionPoint(null);
307 }
308 }
309 }
310
311 private void putBinding(BindingImpl<?> binding) {
312 Key<?> key = binding.getKey();
313 Binding<?> original = bindings.get(key);
314
315 Class<?> rawType = key.getRawType();
316 if (FORBIDDEN_TYPES.contains(rawType)) {
317 addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
318 rawType.getSimpleName());
319 return;
320 }
321
322 if (bindings.containsKey(key)) {
323 addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
324 original.getSource());
325 } else {
326 bindings.put(key, binding);
327 }
328 }
329
330 private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
331
332 @SuppressWarnings("unchecked") // For generic array creation.
333 private static Set<Class<?>> forbiddenTypes() {
334 Set<Class<?>> set = new HashSet<Class<?>>();
335
336 Collections.addAll(set,
337
338 // It's unfortunate that we have to maintain a blacklist of specific
339 // classes, but we can't easily block the whole package because of
340 // all our unit tests.
341
342 AbstractModule.class,
343 Binder.class,
344 Binding.class,
345 Key.class,
346 Module.class,
347 Provider.class,
348 Scope.class,
349 TypeLiteral.class);
350 return Collections.unmodifiableSet(set);
351 }
352
353 interface CreationListener {
354 void notify(InjectorImpl injector);
355 }
356}