blob: b6f11da8dd9440d73bd210b3a71e70c88ef3106a [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>();
limpbizkit51515b52008-03-11 03:46:25 +000044 private final List<ContextualCallable<Void>> eagerSingletonCreators
limpbizkit3d58d6b2008-03-08 16:11:47 +000045 = new ArrayList<ContextualCallable<Void>>();
46 private final Stage stage;
47 private final Map<Key<?>, BindingImpl<?>> bindings;
limpbizkit51515b52008-03-11 03:46:25 +000048 private final Map<Object, Void> outstandingInjections;
limpbizkitf44e9cc2008-03-26 06:33:29 +000049 private final List<Runnable> untargettedBindings = new ArrayList<Runnable>();
limpbizkit3d58d6b2008-03-08 16:11:47 +000050
51 BindCommandProcessor(InjectorImpl injector,
52 Map<Class<? extends Annotation>, Scope> scopes,
53 Stage stage,
limpbizkit51515b52008-03-11 03:46:25 +000054 Map<Key<?>, BindingImpl<?>> bindings,
55 Map<Object, Void> outstandingInjections) {
limpbizkit3d58d6b2008-03-08 16:11:47 +000056 this.injector = injector;
57 this.scopes = scopes;
58 this.stage = stage;
59 this.bindings = bindings;
limpbizkit51515b52008-03-11 03:46:25 +000060 this.outstandingInjections = outstandingInjections;
limpbizkit3d58d6b2008-03-08 16:11:47 +000061 }
62
63 @Override public <T> Boolean visitBind(BindCommand<T> command) {
64 final Object source = command.getSource();
65
66 final Key<T> key = command.getKey();
67 Class<? super T> rawType = key.getTypeLiteral().getRawType();
68
69 if (rawType == Provider.class) {
70 addError(source, ErrorMessages.BINDING_TO_PROVIDER);
71 return true;
72 }
73
74 if (Logger.class == rawType) {
75 // TODO(jessewilson): assert this is coming from the internal module?
76 // addError(source, ErrorMessages.LOGGER_ALREADY_BOUND);
77 // return true;
78 }
79
80 validateKey(command.getSource(), command.getKey());
81
82 // TODO(jessewilson): Scope annotation on type, like @Singleton
83 final boolean shouldPreload = command.getScoping().isEagerSingleton();
84 final Scope scope = command.getScoping().acceptVisitor(new BindScoping.Visitor<Scope>() {
85 public Scope visitEagerSingleton() {
86 return Scopes.SINGLETON;
87 }
88
89 public Scope visitScope(Scope scope) {
90 return scope;
91 }
92
93 public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
94 Scope scope = scopes.get(scopeAnnotation);
95 if (scope != null) {
96 return scope;
97 } else {
98 addError(source, ErrorMessages.SCOPE_NOT_FOUND,
99 "@" + scopeAnnotation.getSimpleName());
100 return Scopes.NO_SCOPE;
101 }
102 }
103
104 public Scope visitNoScoping() {
105 return null;
106 }
107 });
108
109 command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
110 public Void visitToInstance(T instance) {
limpbizkit51515b52008-03-11 03:46:25 +0000111 ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
112 outstandingInjections.put(instance, null);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000113 InternalFactory<? extends T> scopedFactory
114 = Scopes.scope(key, injector, factory, scope);
115 createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
116 injector, key, source, scopedFactory, instance));
117 return null;
118 }
119
120 public Void visitToProvider(Provider<? extends T> provider) {
limpbizkit51515b52008-03-11 03:46:25 +0000121 InternalFactoryToProviderAdapter<? extends T> factory
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122 = new InternalFactoryToProviderAdapter<T>(provider, source);
limpbizkit51515b52008-03-11 03:46:25 +0000123 outstandingInjections.put(provider, null);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000124 InternalFactory<? extends T> scopedFactory
125 = Scopes.scope(key, injector, factory, scope);
126 createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
127 injector, key, source, scopedFactory, scope, provider));
128 return null;
129 }
130
131 public Void visitToProviderKey(Key<? extends Provider<? extends T>> providerKey) {
132 final BoundProviderFactory<T> boundProviderFactory =
133 new BoundProviderFactory<T>(providerKey, source);
134 creationListeners.add(boundProviderFactory);
135 InternalFactory<? extends T> scopedFactory = Scopes.scope(
136 key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
137 createBinding(source, shouldPreload, new LinkedProviderBindingImpl<T>(
138 injector, key, source, scopedFactory, scope, providerKey));
139 return null;
140 }
141
142 public Void visitToKey(Key<? extends T> targetKey) {
143 if (key.equals(targetKey)) {
144 addError(source, ErrorMessages.RECURSIVE_BINDING);
145 }
146
147 FactoryProxy<T> factory = new FactoryProxy<T>(key, targetKey, source);
148 creationListeners.add(factory);
149 InternalFactory<? extends T> scopedFactory
150 = Scopes.scope(key, injector, factory, scope);
151 createBinding(source, shouldPreload, new LinkedBindingImpl<T>(
152 injector, key, source, scopedFactory, scope, targetKey));
153 return null;
154 }
155
156 public Void visitUntargetted() {
limpbizkitf44e9cc2008-03-26 06:33:29 +0000157 final Type type = key.getTypeLiteral().getType();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000158
159 // Error: Missing implementation.
160 // Example: bind(Date.class).annotatedWith(Red.class);
161 // We can't assume abstract types aren't injectable. They may have an
162 // @ImplementedBy annotation or something.
163 if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
164 addError(source, ErrorMessages.MISSING_IMPLEMENTATION);
165 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
166 return null;
167 }
168
limpbizkitf44e9cc2008-03-26 06:33:29 +0000169 untargettedBindings.add(new Runnable() {
170 public void run() {
171 // This cast is safe after the preceeding check.
172 @SuppressWarnings("unchecked")
173 Class<T> clazz = (Class<T>) type;
limpbizkit3d58d6b2008-03-08 16:11:47 +0000174
limpbizkitf44e9cc2008-03-26 06:33:29 +0000175 BindingImpl<T> binding = injector.createBindingFromType(clazz, scope, source);
176 // TODO: Should we clean up the binding left behind in jitBindings?
limpbizkit3d58d6b2008-03-08 16:11:47 +0000177
limpbizkitf44e9cc2008-03-26 06:33:29 +0000178 if (binding == null) {
179 injector.errorHandler.handle(
180 source, ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, clazz);
181 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
182 return;
183 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000184
limpbizkitf44e9cc2008-03-26 06:33:29 +0000185 createBinding(source, shouldPreload, binding);
186 }
187 });
188
limpbizkit3d58d6b2008-03-08 16:11:47 +0000189 return null;
190 }
191 });
192
193 return true;
194 }
195
196 private <T> void validateKey(Object source, Key<T> key) {
197 if (key.hasAnnotationType()) {
198 Class<? extends Annotation> annotationType = key.getAnnotationType();
199
200 if (!Annotations.isRetainedAtRuntime(annotationType)) {
201 addError(StackTraceElements.forType(annotationType),
202 ErrorMessages.MISSING_RUNTIME_RETENTION, source);
203 }
204
205 if (!Key.isBindingAnnotation(annotationType)) {
206 addError(StackTraceElements.forType(annotationType),
207 ErrorMessages.MISSING_BINDING_ANNOTATION, source);
208 }
209 }
210 }
211
212 <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
213 return new InvalidBindingImpl<T>(injector, key, source);
214 }
215
limpbizkit3d58d6b2008-03-08 16:11:47 +0000216 @Override public Boolean visitBindConstant(BindConstantCommand command) {
217 Object value = command.getTarget().get();
218 if (value == null) {
219 addError(command.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
220 }
221
222 validateKey(command.getSource(), command.getKey());
223 ConstantFactory<Object> factory = new ConstantFactory<Object>(value);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000224 putBinding(new ConstantBindingImpl<Object>(
limpbizkit3d58d6b2008-03-08 16:11:47 +0000225 injector, command.getKey(), command.getSource(), factory, value));
226
227 return true;
228 }
229
230 private <T> void createBinding(Object source, boolean shouldPreload,
231 BindingImpl<T> binding) {
232 putBinding(binding);
233
234 // Register to preload if necessary.
235 if (binding.getScope() == Scopes.SINGLETON) {
236 if (stage == Stage.PRODUCTION || shouldPreload) {
limpbizkit51515b52008-03-11 03:46:25 +0000237 eagerSingletonCreators.add(new EagerSingletonCreator(binding.key, binding.internalFactory));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000238 }
239 } else {
240 if (shouldPreload) {
241 addError(source, ErrorMessages.PRELOAD_NOT_ALLOWED);
242 }
243 }
244 }
245
limpbizkitf44e9cc2008-03-26 06:33:29 +0000246 public void createUntargettedBindings() {
247 for (Runnable untargettedBinding : untargettedBindings) {
248 untargettedBinding.run();
249 }
250 }
251
limpbizkit51515b52008-03-11 03:46:25 +0000252 public void createEagerSingletons(InjectorImpl injector) {
253 for (ContextualCallable<Void> preloader : eagerSingletonCreators) {
254 injector.callInContext(preloader);
255 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000256 }
257
258 public void runCreationListeners(InjectorImpl injector) {
259 for (CreationListener creationListener : creationListeners) {
260 creationListener.notify(injector);
261 }
262 }
263
limpbizkit51515b52008-03-11 03:46:25 +0000264 private static class EagerSingletonCreator implements ContextualCallable<Void> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000265 private final Key<?> key;
266 private final InternalFactory<?> factory;
267
limpbizkit51515b52008-03-11 03:46:25 +0000268 public EagerSingletonCreator(Key<?> key, InternalFactory<?> factory) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000269 this.key = key;
270 this.factory = Objects.nonNull(factory, "factory");
271 }
272
273 public Void call(InternalContext context) {
274 InjectionPoint<?> injectionPoint
275 = InjectionPoint.newInstance(key, context.getInjectorImpl());
276 context.setInjectionPoint(injectionPoint);
277 try {
278 factory.get(context, injectionPoint);
279 return null;
280 } catch(ProvisionException provisionException) {
281 provisionException.addContext(injectionPoint);
282 throw provisionException;
283 } finally {
284 context.setInjectionPoint(null);
285 }
286 }
287 }
288
289 private void putBinding(BindingImpl<?> binding) {
290 Key<?> key = binding.getKey();
291 Binding<?> original = bindings.get(key);
292
293 Class<?> rawType = key.getRawType();
294 if (FORBIDDEN_TYPES.contains(rawType)) {
295 addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
296 rawType.getSimpleName());
297 return;
298 }
299
300 if (bindings.containsKey(key)) {
301 addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
302 original.getSource());
303 } else {
304 bindings.put(key, binding);
305 }
306 }
307
308 private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
309
310 @SuppressWarnings("unchecked") // For generic array creation.
311 private static Set<Class<?>> forbiddenTypes() {
312 Set<Class<?>> set = new HashSet<Class<?>>();
313
314 Collections.addAll(set,
315
316 // It's unfortunate that we have to maintain a blacklist of specific
317 // classes, but we can't easily block the whole package because of
318 // all our unit tests.
319
320 AbstractModule.class,
321 Binder.class,
322 Binding.class,
323 Key.class,
324 Module.class,
325 Provider.class,
326 Scope.class,
327 TypeLiteral.class);
328 return Collections.unmodifiableSet(set);
329 }
330
331 interface CreationListener {
332 void notify(InjectorImpl injector);
333 }
334}