blob: ae78278a3eb2a32fb0d3681a3a8d3273773e9f39 [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
kevinb9n1601ae52008-06-03 22:21:04 +000019import com.google.common.collect.Lists;
limpbizkitdf98fcd2008-06-14 05:02:15 +000020import com.google.common.collect.Sets;
limpbizkit3d58d6b2008-03-08 16:11:47 +000021import com.google.inject.commands.BindCommand;
22import com.google.inject.commands.BindConstantCommand;
kevinb9n1601ae52008-06-03 22:21:04 +000023import com.google.inject.commands.BindScoping.Visitor;
limpbizkit3d58d6b2008-03-08 16:11:47 +000024import com.google.inject.commands.BindTarget;
limpbizkit05757142008-06-02 06:58:47 +000025import com.google.inject.internal.Annotations;
limpbizkitdf98fcd2008-06-14 05:02:15 +000026import com.google.inject.internal.ErrorMessage;
limpbizkit05757142008-06-02 06:58:47 +000027import com.google.inject.internal.ResolveFailedException;
28import com.google.inject.internal.StackTraceElements;
limpbizkit3d58d6b2008-03-08 16:11:47 +000029import java.lang.annotation.Annotation;
30import java.lang.reflect.Type;
kevinb9n1601ae52008-06-03 22:21:04 +000031import java.util.Collections;
kevinb9n1601ae52008-06-03 22:21:04 +000032import java.util.List;
33import java.util.Map;
34import java.util.Set;
limpbizkit3d58d6b2008-03-08 16:11:47 +000035
36/**
37 * Handles {@link Binder#bind} and {@link Binder#bindConstant} commands.
38 *
39 * @author crazybob@google.com (Bob Lee)
40 * @author jessewilson@google.com (Jesse Wilson)
41 */
42class BindCommandProcessor extends CommandProcessor {
43
44 private final InjectorImpl injector;
45 private final Map<Class<? extends Annotation>, Scope> scopes;
kevinb9n1601ae52008-06-03 22:21:04 +000046 private final List<CreationListener> creationListeners = Lists.newArrayList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000047 private final Map<Key<?>, BindingImpl<?>> bindings;
kevinb9n1601ae52008-06-03 22:21:04 +000048 private final Set<Object> outstandingInjections;
49 private final List<Runnable> untargettedBindings = Lists.newArrayList();
limpbizkit3d58d6b2008-03-08 16:11:47 +000050
51 BindCommandProcessor(InjectorImpl injector,
52 Map<Class<? extends Annotation>, Scope> scopes,
limpbizkit51515b52008-03-11 03:46:25 +000053 Map<Key<?>, BindingImpl<?>> bindings,
kevinb9n1601ae52008-06-03 22:21:04 +000054 Set<Object> outstandingInjections) {
limpbizkitad94bcb2008-04-28 00:22:43 +000055 super(injector.errorHandler);
limpbizkit3d58d6b2008-03-08 16:11:47 +000056 this.injector = injector;
57 this.scopes = scopes;
limpbizkit3d58d6b2008-03-08 16:11:47 +000058 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) {
limpbizkitdf98fcd2008-06-14 05:02:15 +000069 addError(source, ErrorMessage.bindingToProvider());
limpbizkit3d58d6b2008-03-08 16:11:47 +000070 return true;
71 }
72
limpbizkit3d58d6b2008-03-08 16:11:47 +000073 validateKey(command.getSource(), command.getKey());
74
limpbizkit05757142008-06-02 06:58:47 +000075 final LoadStrategy loadStrategy = command.getScoping().isEagerSingleton()
76 ? LoadStrategy.EAGER
77 : LoadStrategy.LAZY;
kevinb9n1601ae52008-06-03 22:21:04 +000078 final Scope scope = command.getScoping().acceptVisitor(new Visitor<Scope>() {
limpbizkit3d58d6b2008-03-08 16:11:47 +000079 public Scope visitEagerSingleton() {
80 return Scopes.SINGLETON;
81 }
82
83 public Scope visitScope(Scope scope) {
84 return scope;
85 }
86
87 public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
88 Scope scope = scopes.get(scopeAnnotation);
89 if (scope != null) {
90 return scope;
91 } else {
limpbizkitdf98fcd2008-06-14 05:02:15 +000092 addError(source, ErrorMessage.scopeNotFound(
93 "@" + scopeAnnotation.getSimpleName()));
limpbizkit3d58d6b2008-03-08 16:11:47 +000094 return Scopes.NO_SCOPE;
95 }
96 }
97
98 public Scope visitNoScoping() {
99 return null;
100 }
101 });
102
103 command.getTarget().acceptVisitor(new BindTarget.Visitor<T, Void>() {
104 public Void visitToInstance(T instance) {
limpbizkit51515b52008-03-11 03:46:25 +0000105 ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
kevinb9n1601ae52008-06-03 22:21:04 +0000106 outstandingInjections.add(instance);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000107 InternalFactory<? extends T> scopedFactory
108 = Scopes.scope(key, injector, factory, scope);
limpbizkit05757142008-06-02 06:58:47 +0000109 putBinding(new InstanceBindingImpl<T>(
110 injector, key, source, scopedFactory, instance));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000111 return null;
112 }
113
114 public Void visitToProvider(Provider<? extends T> provider) {
limpbizkit51515b52008-03-11 03:46:25 +0000115 InternalFactoryToProviderAdapter<? extends T> factory
limpbizkit3d58d6b2008-03-08 16:11:47 +0000116 = new InternalFactoryToProviderAdapter<T>(provider, source);
kevinb9n1601ae52008-06-03 22:21:04 +0000117 outstandingInjections.add(provider);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000118 InternalFactory<? extends T> scopedFactory
119 = Scopes.scope(key, injector, factory, scope);
limpbizkit05757142008-06-02 06:58:47 +0000120 putBinding(new ProviderInstanceBindingImpl<T>(
121 injector, key, source, scopedFactory, scope, provider, loadStrategy));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122 return null;
123 }
124
125 public Void visitToProviderKey(Key<? extends Provider<? extends T>> providerKey) {
126 final BoundProviderFactory<T> boundProviderFactory =
127 new BoundProviderFactory<T>(providerKey, source);
128 creationListeners.add(boundProviderFactory);
129 InternalFactory<? extends T> scopedFactory = Scopes.scope(
130 key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
limpbizkit05757142008-06-02 06:58:47 +0000131 putBinding(new LinkedProviderBindingImpl<T>(
132 injector, key, source, scopedFactory, scope, providerKey, loadStrategy));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000133 return null;
134 }
135
136 public Void visitToKey(Key<? extends T> targetKey) {
137 if (key.equals(targetKey)) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000138 addError(source, ErrorMessage.recursiveBinding());
limpbizkit3d58d6b2008-03-08 16:11:47 +0000139 }
140
141 FactoryProxy<T> factory = new FactoryProxy<T>(key, targetKey, source);
142 creationListeners.add(factory);
143 InternalFactory<? extends T> scopedFactory
144 = Scopes.scope(key, injector, factory, scope);
limpbizkit05757142008-06-02 06:58:47 +0000145 putBinding(new LinkedBindingImpl<T>(
146 injector, key, source, scopedFactory, scope, targetKey, loadStrategy));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000147 return null;
148 }
149
150 public Void visitUntargetted() {
limpbizkitf44e9cc2008-03-26 06:33:29 +0000151 final Type type = key.getTypeLiteral().getType();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000152
153 // Error: Missing implementation.
154 // Example: bind(Date.class).annotatedWith(Red.class);
155 // We can't assume abstract types aren't injectable. They may have an
156 // @ImplementedBy annotation or something.
157 if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000158 addError(source, ErrorMessage.missingImplementation());
limpbizkit05757142008-06-02 06:58:47 +0000159 putBinding(invalidBinding(injector, key, source));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000160 return null;
161 }
162
limpbizkit51411872008-05-13 22:15:29 +0000163 // This cast is safe after the preceeding check.
164 @SuppressWarnings("unchecked")
165 Class<T> clazz = (Class<T>) type;
166 final BindingImpl<T> binding;
167 try {
limpbizkit05757142008-06-02 06:58:47 +0000168 binding = injector.createUnitializedBinding(clazz, scope, source, loadStrategy);
169 putBinding(binding);
limpbizkit51411872008-05-13 22:15:29 +0000170 } catch (ResolveFailedException e) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000171 injector.errorHandler.handle(e.getMessage(source));
limpbizkit05757142008-06-02 06:58:47 +0000172 putBinding(invalidBinding(injector, key, source));
limpbizkit51411872008-05-13 22:15:29 +0000173 return null;
174 }
175
limpbizkitf44e9cc2008-03-26 06:33:29 +0000176 untargettedBindings.add(new Runnable() {
177 public void run() {
limpbizkit3b1cd582008-04-28 00:06:01 +0000178 try {
limpbizkit51411872008-05-13 22:15:29 +0000179 injector.initializeBinding(binding);
limpbizkit3b1cd582008-04-28 00:06:01 +0000180 } catch (ResolveFailedException e) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000181 injector.errorHandler.handle(e.getMessage(source));
limpbizkitf44e9cc2008-03-26 06:33:29 +0000182 }
limpbizkitf44e9cc2008-03-26 06:33:29 +0000183 }
184 });
185
limpbizkit3d58d6b2008-03-08 16:11:47 +0000186 return null;
187 }
188 });
189
190 return true;
191 }
192
193 private <T> void validateKey(Object source, Key<T> key) {
194 if (key.hasAnnotationType()) {
195 Class<? extends Annotation> annotationType = key.getAnnotationType();
196
197 if (!Annotations.isRetainedAtRuntime(annotationType)) {
198 addError(StackTraceElements.forType(annotationType),
limpbizkitdf98fcd2008-06-14 05:02:15 +0000199 ErrorMessage.missingRuntimeRetention(source));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000200 }
201
202 if (!Key.isBindingAnnotation(annotationType)) {
203 addError(StackTraceElements.forType(annotationType),
limpbizkitdf98fcd2008-06-14 05:02:15 +0000204 ErrorMessage.missingBindingAnnotation(source));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000205 }
206 }
207 }
208
209 <T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
210 return new InvalidBindingImpl<T>(injector, key, source);
211 }
212
limpbizkit3d58d6b2008-03-08 16:11:47 +0000213 @Override public Boolean visitBindConstant(BindConstantCommand command) {
limpbizkitd6967b92008-05-16 15:28:51 +0000214 BindTarget<?> target = command.getTarget();
215 if (target == null) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000216 addError(command.getSource(), ErrorMessage.missingConstantValues());
limpbizkitd6967b92008-05-16 15:28:51 +0000217 return true;
limpbizkit3d58d6b2008-03-08 16:11:47 +0000218 }
219
limpbizkitd6967b92008-05-16 15:28:51 +0000220 Object value = target.get();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000221 validateKey(command.getSource(), command.getKey());
222 ConstantFactory<Object> factory = new ConstantFactory<Object>(value);
limpbizkitf44e9cc2008-03-26 06:33:29 +0000223 putBinding(new ConstantBindingImpl<Object>(
limpbizkit3d58d6b2008-03-08 16:11:47 +0000224 injector, command.getKey(), command.getSource(), factory, value));
225
226 return true;
227 }
228
limpbizkitf44e9cc2008-03-26 06:33:29 +0000229 public void createUntargettedBindings() {
230 for (Runnable untargettedBinding : untargettedBindings) {
231 untargettedBinding.run();
232 }
233 }
234
limpbizkit3d58d6b2008-03-08 16:11:47 +0000235 public void runCreationListeners(InjectorImpl injector) {
236 for (CreationListener creationListener : creationListeners) {
237 creationListener.notify(injector);
238 }
239 }
240
limpbizkit3d58d6b2008-03-08 16:11:47 +0000241 private void putBinding(BindingImpl<?> binding) {
242 Key<?> key = binding.getKey();
243 Binding<?> original = bindings.get(key);
244
245 Class<?> rawType = key.getRawType();
246 if (FORBIDDEN_TYPES.contains(rawType)) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000247 addError(binding.getSource(), ErrorMessage.cannotBindToGuiceType(
248 rawType.getSimpleName()));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000249 return;
250 }
251
252 if (bindings.containsKey(key)) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000253 addError(binding.getSource(), ErrorMessage.bindingAlreadySet(key,
254 original.getSource()));
limpbizkit3d58d6b2008-03-08 16:11:47 +0000255 } else {
256 bindings.put(key, binding);
257 }
258 }
259
260 private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
261
262 @SuppressWarnings("unchecked") // For generic array creation.
263 private static Set<Class<?>> forbiddenTypes() {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000264 Set<Class<?>> set = Sets.newHashSet();
limpbizkit3d58d6b2008-03-08 16:11:47 +0000265
266 Collections.addAll(set,
267
268 // It's unfortunate that we have to maintain a blacklist of specific
269 // classes, but we can't easily block the whole package because of
270 // all our unit tests.
271
272 AbstractModule.class,
273 Binder.class,
274 Binding.class,
275 Key.class,
276 Module.class,
277 Provider.class,
278 Scope.class,
279 TypeLiteral.class);
280 return Collections.unmodifiableSet(set);
281 }
282
283 interface CreationListener {
284 void notify(InjectorImpl injector);
285 }
286}