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