blob: 008f6c8275da7703ca8218810f41cc4dc6712e25 [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;
limpbizkit3b1cd582008-04-28 00:06:01 +000023import com.google.inject.internal.*;
limpbizkit3d58d6b2008-03-08 16:11:47 +000024
25import java.lang.annotation.Annotation;
26import java.lang.reflect.Type;
27import java.util.*;
limpbizkit3d58d6b2008-03-08 16:11:47 +000028
29/**
30 * Handles {@link Binder#bind} and {@link Binder#bindConstant} commands.
31 *
32 * @author crazybob@google.com (Bob Lee)
33 * @author jessewilson@google.com (Jesse Wilson)
34 */
35class BindCommandProcessor extends CommandProcessor {
36
37 private final InjectorImpl injector;
38 private final Map<Class<? extends Annotation>, Scope> scopes;
39 private final List<CreationListener> creationListeners
40 = new ArrayList<CreationListener>();
limpbizkit51515b52008-03-11 03:46:25 +000041 private final List<ContextualCallable<Void>> eagerSingletonCreators
limpbizkit3d58d6b2008-03-08 16:11:47 +000042 = new ArrayList<ContextualCallable<Void>>();
43 private final Stage stage;
44 private final Map<Key<?>, BindingImpl<?>> bindings;
limpbizkit51515b52008-03-11 03:46:25 +000045 private final Map<Object, Void> outstandingInjections;
limpbizkitf44e9cc2008-03-26 06:33:29 +000046 private final List<Runnable> untargettedBindings = new ArrayList<Runnable>();
limpbizkit3d58d6b2008-03-08 16:11:47 +000047
48 BindCommandProcessor(InjectorImpl injector,
49 Map<Class<? extends Annotation>, Scope> scopes,
50 Stage stage,
limpbizkit51515b52008-03-11 03:46:25 +000051 Map<Key<?>, BindingImpl<?>> bindings,
52 Map<Object, Void> outstandingInjections) {
limpbizkitad94bcb2008-04-28 00:22:43 +000053 super(injector.errorHandler);
limpbizkit3d58d6b2008-03-08 16:11:47 +000054 this.injector = injector;
55 this.scopes = scopes;
56 this.stage = stage;
57 this.bindings = bindings;
limpbizkit51515b52008-03-11 03:46:25 +000058 this.outstandingInjections = outstandingInjections;
limpbizkit3d58d6b2008-03-08 16:11:47 +000059 }
60
61 @Override public <T> Boolean visitBind(BindCommand<T> command) {
62 final Object source = command.getSource();
63
64 final Key<T> key = command.getKey();
65 Class<? super T> rawType = key.getTypeLiteral().getRawType();
66
67 if (rawType == Provider.class) {
68 addError(source, ErrorMessages.BINDING_TO_PROVIDER);
69 return true;
70 }
71
limpbizkit3d58d6b2008-03-08 16:11:47 +000072 validateKey(command.getSource(), command.getKey());
73
74 // TODO(jessewilson): Scope annotation on type, like @Singleton
75 final boolean shouldPreload = command.getScoping().isEagerSingleton();
76 final Scope scope = command.getScoping().acceptVisitor(new BindScoping.Visitor<Scope>() {
77 public Scope visitEagerSingleton() {
78 return Scopes.SINGLETON;
79 }
80
81 public Scope visitScope(Scope scope) {
82 return scope;
83 }
84
85 public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
86 Scope scope = scopes.get(scopeAnnotation);
87 if (scope != null) {
88 return scope;
89 } else {
90 addError(source, ErrorMessages.SCOPE_NOT_FOUND,
91 "@" + scopeAnnotation.getSimpleName());
92 return Scopes.NO_SCOPE;
93 }
94 }
95
96 public Scope visitNoScoping() {
97 return null;
98 }
99 });
100
101 command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
102 public Void visitToInstance(T instance) {
limpbizkit51515b52008-03-11 03:46:25 +0000103 ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
104 outstandingInjections.put(instance, null);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000105 InternalFactory<? extends T> scopedFactory
106 = Scopes.scope(key, injector, factory, scope);
107 createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
108 injector, key, source, scopedFactory, instance));
109 return null;
110 }
111
112 public Void visitToProvider(Provider<? extends T> provider) {
limpbizkit51515b52008-03-11 03:46:25 +0000113 InternalFactoryToProviderAdapter<? extends T> factory
limpbizkit3d58d6b2008-03-08 16:11:47 +0000114 = new InternalFactoryToProviderAdapter<T>(provider, source);
limpbizkit51515b52008-03-11 03:46:25 +0000115 outstandingInjections.put(provider, null);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000116 InternalFactory<? extends T> scopedFactory
117 = Scopes.scope(key, injector, factory, scope);
118 createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
119 injector, key, source, scopedFactory, scope, provider));
120 return null;
121 }
122
123 public Void visitToProviderKey(Key<? extends Provider<? extends T>> providerKey) {
124 final BoundProviderFactory<T> boundProviderFactory =
125 new BoundProviderFactory<T>(providerKey, source);
126 creationListeners.add(boundProviderFactory);
127 InternalFactory<? extends T> scopedFactory = Scopes.scope(
128 key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
129 createBinding(source, shouldPreload, new LinkedProviderBindingImpl<T>(
130 injector, key, source, scopedFactory, scope, providerKey));
131 return null;
132 }
133
134 public Void visitToKey(Key<? extends T> targetKey) {
135 if (key.equals(targetKey)) {
136 addError(source, ErrorMessages.RECURSIVE_BINDING);
137 }
138
139 FactoryProxy<T> factory = new FactoryProxy<T>(key, targetKey, source);
140 creationListeners.add(factory);
141 InternalFactory<? extends T> scopedFactory
142 = Scopes.scope(key, injector, factory, scope);
143 createBinding(source, shouldPreload, new LinkedBindingImpl<T>(
144 injector, key, source, scopedFactory, scope, targetKey));
145 return null;
146 }
147
148 public Void visitUntargetted() {
limpbizkitf44e9cc2008-03-26 06:33:29 +0000149 final Type type = key.getTypeLiteral().getType();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000150
151 // Error: Missing implementation.
152 // Example: bind(Date.class).annotatedWith(Red.class);
153 // We can't assume abstract types aren't injectable. They may have an
154 // @ImplementedBy annotation or something.
155 if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
156 addError(source, ErrorMessages.MISSING_IMPLEMENTATION);
157 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
158 return null;
159 }
160
limpbizkit51411872008-05-13 22:15:29 +0000161 // This cast is safe after the preceeding check.
162 @SuppressWarnings("unchecked")
163 Class<T> clazz = (Class<T>) type;
164 final BindingImpl<T> binding;
165 try {
166 binding = injector.createUnitializedBinding(clazz, scope, source);
167 createBinding(source, shouldPreload, binding);
168 } catch (ResolveFailedException e) {
169 injector.errorHandler.handle(source, e.getMessage());
170 createBinding(source, shouldPreload, invalidBinding(injector, key, source));
171 return null;
172 }
173
limpbizkitf44e9cc2008-03-26 06:33:29 +0000174 untargettedBindings.add(new Runnable() {
175 public void run() {
limpbizkit3b1cd582008-04-28 00:06:01 +0000176 try {
limpbizkit51411872008-05-13 22:15:29 +0000177 injector.initializeBinding(binding);
limpbizkit3b1cd582008-04-28 00:06:01 +0000178 } catch (ResolveFailedException e) {
179 injector.errorHandler.handle(source, e.getMessage());
limpbizkitf44e9cc2008-03-26 06:33:29 +0000180 }
limpbizkitf44e9cc2008-03-26 06:33:29 +0000181 }
182 });
183
limpbizkit3d58d6b2008-03-08 16:11:47 +0000184 return null;
185 }
186 });
187
188 return true;
189 }
190
191 private <T> void validateKey(Object source, Key<T> key) {
192 if (key.hasAnnotationType()) {
193 Class<? extends Annotation> annotationType = key.getAnnotationType();
194
195 if (!Annotations.isRetainedAtRuntime(annotationType)) {
196 addError(StackTraceElements.forType(annotationType),
197 ErrorMessages.MISSING_RUNTIME_RETENTION, source);
198 }
199
200 if (!Key.isBindingAnnotation(annotationType)) {
201 addError(StackTraceElements.forType(annotationType),
202 ErrorMessages.MISSING_BINDING_ANNOTATION, source);
203 }
204 }
205 }
206
207 <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
208 return new InvalidBindingImpl<T>(injector, key, source);
209 }
210
limpbizkit3d58d6b2008-03-08 16:11:47 +0000211 @Override public Boolean visitBindConstant(BindConstantCommand command) {
limpbizkitd6967b92008-05-16 15:28:51 +0000212 BindTarget<?> target = command.getTarget();
213 if (target == null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000214 addError(command.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
limpbizkitd6967b92008-05-16 15:28:51 +0000215 return true;
limpbizkit3d58d6b2008-03-08 16:11:47 +0000216 }
217
limpbizkitd6967b92008-05-16 15:28:51 +0000218 Object value = target.get();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000219 validateKey(command.getSource(), command.getKey());
220 ConstantFactory<Object> factory = new ConstantFactory<Object>(value);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000221 putBinding(new ConstantBindingImpl<Object>(
limpbizkit3d58d6b2008-03-08 16:11:47 +0000222 injector, command.getKey(), command.getSource(), factory, value));
223
224 return true;
225 }
226
227 private <T> void createBinding(Object source, boolean shouldPreload,
228 BindingImpl<T> binding) {
229 putBinding(binding);
230
231 // Register to preload if necessary.
232 if (binding.getScope() == Scopes.SINGLETON) {
233 if (stage == Stage.PRODUCTION || shouldPreload) {
limpbizkit51515b52008-03-11 03:46:25 +0000234 eagerSingletonCreators.add(new EagerSingletonCreator(binding.key, binding.internalFactory));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000235 }
236 } else {
237 if (shouldPreload) {
238 addError(source, ErrorMessages.PRELOAD_NOT_ALLOWED);
239 }
240 }
241 }
242
limpbizkitf44e9cc2008-03-26 06:33:29 +0000243 public void createUntargettedBindings() {
244 for (Runnable untargettedBinding : untargettedBindings) {
245 untargettedBinding.run();
246 }
247 }
248
limpbizkit51515b52008-03-11 03:46:25 +0000249 public void createEagerSingletons(InjectorImpl injector) {
250 for (ContextualCallable<Void> preloader : eagerSingletonCreators) {
251 injector.callInContext(preloader);
252 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000253 }
254
255 public void runCreationListeners(InjectorImpl injector) {
256 for (CreationListener creationListener : creationListeners) {
257 creationListener.notify(injector);
258 }
259 }
260
limpbizkit51515b52008-03-11 03:46:25 +0000261 private static class EagerSingletonCreator implements ContextualCallable<Void> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000262 private final Key<?> key;
263 private final InternalFactory<?> factory;
264
limpbizkit51515b52008-03-11 03:46:25 +0000265 public EagerSingletonCreator(Key<?> key, InternalFactory<?> factory) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000266 this.key = key;
267 this.factory = Objects.nonNull(factory, "factory");
268 }
269
270 public Void call(InternalContext context) {
271 InjectionPoint<?> injectionPoint
limpbizkit8b237452008-04-22 06:47:36 +0000272 = InjectionPoint.newInstance(key, context.getInjector());
limpbizkit3d58d6b2008-03-08 16:11:47 +0000273 context.setInjectionPoint(injectionPoint);
274 try {
275 factory.get(context, injectionPoint);
276 return null;
277 } catch(ProvisionException provisionException) {
278 provisionException.addContext(injectionPoint);
279 throw provisionException;
280 } finally {
281 context.setInjectionPoint(null);
282 }
283 }
284 }
285
286 private void putBinding(BindingImpl<?> binding) {
287 Key<?> key = binding.getKey();
288 Binding<?> original = bindings.get(key);
289
290 Class<?> rawType = key.getRawType();
291 if (FORBIDDEN_TYPES.contains(rawType)) {
292 addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
293 rawType.getSimpleName());
294 return;
295 }
296
297 if (bindings.containsKey(key)) {
298 addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
299 original.getSource());
300 } else {
301 bindings.put(key, binding);
302 }
303 }
304
305 private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
306
307 @SuppressWarnings("unchecked") // For generic array creation.
308 private static Set<Class<?>> forbiddenTypes() {
309 Set<Class<?>> set = new HashSet<Class<?>>();
310
311 Collections.addAll(set,
312
313 // It's unfortunate that we have to maintain a blacklist of specific
314 // classes, but we can't easily block the whole package because of
315 // all our unit tests.
316
317 AbstractModule.class,
318 Binder.class,
319 Binding.class,
320 Key.class,
321 Module.class,
322 Provider.class,
323 Scope.class,
324 TypeLiteral.class);
325 return Collections.unmodifiableSet(set);
326 }
327
328 interface CreationListener {
329 void notify(InjectorImpl injector);
330 }
331}