blob: e012873fc2080acd89787b9d2169d902a813d56d [file] [log] [blame]
limpbizkita67e39f2008-02-18 20:10:59 +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.commands;
18
limpbizkit3d58d6b2008-03-08 16:11:47 +000019import com.google.inject.*;
limpbizkita67e39f2008-02-18 20:10:59 +000020import com.google.inject.binder.AnnotatedBindingBuilder;
21import com.google.inject.binder.ConstantBindingBuilder;
22import com.google.inject.binder.LinkedBindingBuilder;
23import com.google.inject.binder.ScopedBindingBuilder;
limpbizkit8b237452008-04-22 06:47:36 +000024import com.google.inject.internal.ErrorMessages;
limpbizkitd6967b92008-05-16 15:28:51 +000025import com.google.inject.spi.SourceProviders;
kevinb9n1601ae52008-06-03 22:21:04 +000026import static com.google.common.base.Preconditions.checkNotNull;
limpbizkita67e39f2008-02-18 20:10:59 +000027
28import java.lang.annotation.Annotation;
29
30/**
31 * Immutable snapshot of a request to bind a value.
32 *
33 * @author jessewilson@google.com (Jesse Wilson)
34 */
35public final class BindCommand<T> implements Command {
limpbizkita67e39f2008-02-18 20:10:59 +000036
limpbizkit3d58d6b2008-03-08 16:11:47 +000037 static {
38 SourceProviders.skip(BindCommand.BindingBuilder.class);
39 }
40
41 private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
42 public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
43 return linkedBindingBuilder;
44 }
45 public <V> V acceptVisitor(Visitor<Object, V> visitor) {
46 return visitor.visitUntargetted();
47 }
48 };
49
50 private static final BindScoping EMPTY_SCOPING = new AbstractScoping() {
51 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
52 // do nothing
53 }
54 public <V> V acceptVisitor(Visitor<V> visitor) {
55 return visitor.visitNoScoping();
56 }
57 };
58
59 private final Object source;
60 private Key<T> key;
kevinb9n1601ae52008-06-03 22:21:04 +000061
62 @SuppressWarnings("unchecked")
limpbizkit3d58d6b2008-03-08 16:11:47 +000063 private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
64 private BindScoping bindScoping = EMPTY_SCOPING;
65
66 BindCommand(Object source, Key<T> key) {
kevinb9n1601ae52008-06-03 22:21:04 +000067 this.source = checkNotNull(source, "source");
68 this.key = checkNotNull(key, "key");
limpbizkita67e39f2008-02-18 20:10:59 +000069 }
70
limpbizkit3d58d6b2008-03-08 16:11:47 +000071 public Object getSource() {
72 return source;
73 }
74
limpbizkita67e39f2008-02-18 20:10:59 +000075 public <V> V acceptVisitor(Visitor<V> visitor) {
76 return visitor.visitBind(this);
77 }
78
79 public Key<T> getKey() {
80 return key;
81 }
82
83 public BindTarget<T> getTarget() {
84 return bindTarget;
85 }
86
87 public BindScoping getScoping() {
88 return bindScoping;
89 }
90
91 @Override public String toString() {
92 return "bind " + key
limpbizkit3d58d6b2008-03-08 16:11:47 +000093 + (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
94 + (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
limpbizkita67e39f2008-02-18 20:10:59 +000095 }
96
97 private static abstract class AbstractTarget<T> implements BindTarget<T> {
98 public void execute(ConstantBindingBuilder builder) {
99 throw new UnsupportedOperationException();
100 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000101 public T get() {
102 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000103 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000104 public Key<? extends Provider<? extends T>> getProviderKey() {
105 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000106 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000107 public Provider<? extends T> getProvider() {
108 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000109 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000110 public Key<? extends T> getKey() {
111 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000112 }
113 }
114
115 private static abstract class AbstractScoping implements BindScoping {
116 public boolean isEagerSingleton() {
117 return false;
118 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000119 public Scope getScope() {
120 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000121 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122 public Class<? extends Annotation> getScopeAnnotation() {
123 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000124 }
125 }
126
limpbizkit3d58d6b2008-03-08 16:11:47 +0000127 BindingBuilder bindingBuilder(Binder binder) {
128 return new BindingBuilder(binder);
limpbizkita67e39f2008-02-18 20:10:59 +0000129 }
130
131 /**
132 * Package-private write access to the internal state of this command.
133 */
134 class BindingBuilder implements AnnotatedBindingBuilder<T> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000135 private final Binder binder;
136
137 BindingBuilder(Binder binder) {
138 this.binder = binder;
139 }
140
limpbizkita67e39f2008-02-18 20:10:59 +0000141 public LinkedBindingBuilder<T> annotatedWith(
142 Class<? extends Annotation> annotationType) {
kevinb9n1601ae52008-06-03 22:21:04 +0000143 checkNotNull(annotationType, "annotationType");
limpbizkit7f8eda02008-03-26 01:11:16 +0000144 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000145 key = Key.get(key.getTypeLiteral(), annotationType);
146 return this;
147 }
148
149 public LinkedBindingBuilder<T> annotatedWith(Annotation annotation) {
kevinb9n1601ae52008-06-03 22:21:04 +0000150 checkNotNull(annotation, "annotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000151 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000152 key = Key.get(key.getTypeLiteral(), annotation);
153 return this;
154 }
155
156 public ScopedBindingBuilder to(final Class<? extends T> implementation) {
157 return to(Key.get(implementation));
158 }
159
160 public ScopedBindingBuilder to(
161 final TypeLiteral<? extends T> implementation) {
162 return to(Key.get(implementation));
163 }
164
165 public ScopedBindingBuilder to(final Key<? extends T> targetKey) {
kevinb9n1601ae52008-06-03 22:21:04 +0000166 checkNotNull(targetKey, "targetKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000167 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000168 bindTarget = new AbstractTarget<T>() {
169 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
170 return linkedBindingBuilder.to(targetKey);
171 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000172 @Override public Key<? extends T> getKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000173 return targetKey;
174 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000175 public <V> V acceptVisitor(Visitor<T, V> visitor) {
176 return visitor.visitToKey(targetKey);
177 }
limpbizkita67e39f2008-02-18 20:10:59 +0000178 @Override public String toString() {
179 return String.valueOf(targetKey);
180 }
181 };
182 return this;
183 }
184
185 public void toInstance(final T instance) {
kevinb9n1601ae52008-06-03 22:21:04 +0000186 checkNotNull(instance, ErrorMessages.CANNOT_BIND_TO_NULL_INSTANCE);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000187
limpbizkit7f8eda02008-03-26 01:11:16 +0000188 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000189 bindTarget = new AbstractTarget<T>() {
190 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
191 linkedBindingBuilder.toInstance(instance);
192 return null;
193 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000194 @Override public T get() {
limpbizkita67e39f2008-02-18 20:10:59 +0000195 return instance;
196 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000197 public <V> V acceptVisitor(Visitor<T, V> visitor) {
198 return visitor.visitToInstance(instance);
199 }
limpbizkita67e39f2008-02-18 20:10:59 +0000200 @Override public String toString() {
201 return "instance " + instance;
202 }
203 };
204 }
205
206 public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
kevinb9n1601ae52008-06-03 22:21:04 +0000207 checkNotNull(provider, "provider");
limpbizkit7f8eda02008-03-26 01:11:16 +0000208 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000209 bindTarget = new AbstractTarget<T>() {
210 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
211 return linkedBindingBuilder.toProvider(provider);
212 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000213 @Override public Provider<? extends T> getProvider() {
limpbizkita67e39f2008-02-18 20:10:59 +0000214 return provider;
215 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000216 public <V> V acceptVisitor(Visitor<T, V> visitor) {
217 return visitor.visitToProvider(provider);
218 }
limpbizkita67e39f2008-02-18 20:10:59 +0000219 @Override public String toString() {
220 return "provider " + provider;
221 }
222 };
223 return this;
224 }
225
226 public ScopedBindingBuilder toProvider(
227 Class<? extends Provider<? extends T>> providerType) {
228 return toProvider(Key.get(providerType));
229 }
230
231 public ScopedBindingBuilder toProvider(
232 final Key<? extends Provider<? extends T>> providerKey) {
kevinb9n1601ae52008-06-03 22:21:04 +0000233 checkNotNull(providerKey, "providerKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000234 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000235 bindTarget = new AbstractTarget<T>() {
236 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
237 return linkedBindingBuilder.toProvider(providerKey);
238 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000239 @Override public Key<? extends Provider<? extends T>> getProviderKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000240 return providerKey;
241 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000242 public <V> V acceptVisitor(Visitor<T, V> visitor) {
243 return visitor.visitToProviderKey(providerKey);
244 }
limpbizkita67e39f2008-02-18 20:10:59 +0000245 @Override public String toString() {
246 return "provider " + providerKey;
247 }
248 };
249 return this;
250 }
251
252 public void in(final Class<? extends Annotation> scopeAnnotation) {
kevinb9n1601ae52008-06-03 22:21:04 +0000253 checkNotNull(scopeAnnotation, "scopeAnnotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000254 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000255
256 bindScoping = new AbstractScoping() {
257 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
258 scopedBindingBuilder.in(scopeAnnotation);
259 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000260 @Override public Class<? extends Annotation> getScopeAnnotation() {
limpbizkita67e39f2008-02-18 20:10:59 +0000261 return scopeAnnotation;
262 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000263 public <V> V acceptVisitor(Visitor<V> visitor) {
264 return visitor.visitScopeAnnotation(scopeAnnotation);
265 }
limpbizkita67e39f2008-02-18 20:10:59 +0000266 @Override public String toString() {
267 return scopeAnnotation.getName();
268 }
269 };
270 }
271
272 public void in(final Scope scope) {
kevinb9n1601ae52008-06-03 22:21:04 +0000273 checkNotNull(scope, "scope");
limpbizkit7f8eda02008-03-26 01:11:16 +0000274 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000275 bindScoping = new AbstractScoping() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000276
limpbizkita67e39f2008-02-18 20:10:59 +0000277 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
278 scopedBindingBuilder.in(scope);
279 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000280 @Override public Scope getScope() {
limpbizkita67e39f2008-02-18 20:10:59 +0000281 return scope;
282 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000283 public <V> V acceptVisitor(Visitor<V> visitor) {
284 return visitor.visitScope(scope);
285 }
limpbizkita67e39f2008-02-18 20:10:59 +0000286 @Override public String toString() {
287 return String.valueOf(scope);
288 }
289 };
290 }
291
292 public void asEagerSingleton() {
limpbizkit7f8eda02008-03-26 01:11:16 +0000293 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000294 bindScoping = new AbstractScoping() {
295 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
296 scopedBindingBuilder.asEagerSingleton();
297 }
298 @Override public boolean isEagerSingleton() {
299 return true;
300 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000301 public <V> V acceptVisitor(Visitor<V> visitor) {
302 return visitor.visitEagerSingleton();
303 }
limpbizkita67e39f2008-02-18 20:10:59 +0000304 @Override public String toString() {
305 return "eager singleton";
306 }
307 };
308 }
309
limpbizkit3d58d6b2008-03-08 16:11:47 +0000310 static final String IMPLEMENTATION_ALREADY_SET
311 = "Implementation is set more than once.";
312 static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
313 + " permitted when binding to a single instance.";
314 static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
315 static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
316 + " is specified for this binding.";
317
limpbizkit7f8eda02008-03-26 01:11:16 +0000318 private void checkNotTargetted() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000319 if (bindTarget != EMPTY_BIND_TARGET) {
320 binder.addError(IMPLEMENTATION_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000321 }
322 }
323
limpbizkit7f8eda02008-03-26 01:11:16 +0000324 private void checkNotAnnotated() {
limpbizkita67e39f2008-02-18 20:10:59 +0000325 if (BindCommand.this.key.getAnnotationType() != null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000326 binder.addError(ANNOTATION_ALREADY_SPECIFIED);
limpbizkita67e39f2008-02-18 20:10:59 +0000327 }
328 }
329
limpbizkit7f8eda02008-03-26 01:11:16 +0000330 private void checkNotScoped() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000331 // Scoping isn't allowed when we have only one instance.
332 if (bindTarget.get() != null) {
333 binder.addError(SINGLE_INSTANCE_AND_SCOPE);
334 return;
335 }
336
337 if (bindScoping != EMPTY_SCOPING) {
338 binder.addError(SCOPE_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000339 }
340 }
limpbizkitd6967b92008-05-16 15:28:51 +0000341
342 @Override public String toString() {
343 String type = key.getAnnotationType() == null
344 ? "AnnotatedBindingBuilder<"
345 : "LinkedBindingBuilder<";
346 return type + key.getTypeLiteral() + ">";
347 }
limpbizkita67e39f2008-02-18 20:10:59 +0000348 }
349}