blob: 9adb02583dbfc47f73504e646d6b5f92297d6bf7 [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
limpbizkitdf98fcd2008-06-14 05:02:15 +000019import static com.google.common.base.Preconditions.checkNotNull;
20import com.google.inject.Binder;
21import com.google.inject.Key;
22import com.google.inject.Provider;
23import com.google.inject.Scope;
24import com.google.inject.TypeLiteral;
limpbizkita67e39f2008-02-18 20:10:59 +000025import com.google.inject.binder.AnnotatedBindingBuilder;
26import com.google.inject.binder.ConstantBindingBuilder;
27import com.google.inject.binder.LinkedBindingBuilder;
28import com.google.inject.binder.ScopedBindingBuilder;
limpbizkitdf98fcd2008-06-14 05:02:15 +000029import com.google.inject.internal.ErrorMessage;
limpbizkitd6967b92008-05-16 15:28:51 +000030import com.google.inject.spi.SourceProviders;
limpbizkita67e39f2008-02-18 20:10:59 +000031import java.lang.annotation.Annotation;
32
33/**
34 * Immutable snapshot of a request to bind a value.
35 *
36 * @author jessewilson@google.com (Jesse Wilson)
37 */
38public final class BindCommand<T> implements Command {
limpbizkita67e39f2008-02-18 20:10:59 +000039
limpbizkit3d58d6b2008-03-08 16:11:47 +000040 static {
41 SourceProviders.skip(BindCommand.BindingBuilder.class);
42 }
43
44 private static final BindTarget<Object> EMPTY_BIND_TARGET = new AbstractTarget<Object>() {
45 public ScopedBindingBuilder execute(LinkedBindingBuilder<Object> linkedBindingBuilder) {
46 return linkedBindingBuilder;
47 }
48 public <V> V acceptVisitor(Visitor<Object, V> visitor) {
49 return visitor.visitUntargetted();
50 }
51 };
52
53 private static final BindScoping EMPTY_SCOPING = new AbstractScoping() {
54 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
55 // do nothing
56 }
57 public <V> V acceptVisitor(Visitor<V> visitor) {
58 return visitor.visitNoScoping();
59 }
60 };
61
62 private final Object source;
63 private Key<T> key;
kevinb9n1601ae52008-06-03 22:21:04 +000064
65 @SuppressWarnings("unchecked")
limpbizkit3d58d6b2008-03-08 16:11:47 +000066 private BindTarget<T> bindTarget = (BindTarget<T>) EMPTY_BIND_TARGET;
67 private BindScoping bindScoping = EMPTY_SCOPING;
68
69 BindCommand(Object source, Key<T> key) {
kevinb9n1601ae52008-06-03 22:21:04 +000070 this.source = checkNotNull(source, "source");
71 this.key = checkNotNull(key, "key");
limpbizkita67e39f2008-02-18 20:10:59 +000072 }
73
limpbizkit3d58d6b2008-03-08 16:11:47 +000074 public Object getSource() {
75 return source;
76 }
77
limpbizkita67e39f2008-02-18 20:10:59 +000078 public <V> V acceptVisitor(Visitor<V> visitor) {
79 return visitor.visitBind(this);
80 }
81
82 public Key<T> getKey() {
83 return key;
84 }
85
86 public BindTarget<T> getTarget() {
87 return bindTarget;
88 }
89
90 public BindScoping getScoping() {
91 return bindScoping;
92 }
93
94 @Override public String toString() {
95 return "bind " + key
limpbizkit3d58d6b2008-03-08 16:11:47 +000096 + (bindTarget == EMPTY_BIND_TARGET ? "" : (" to " + bindTarget))
97 + (bindScoping == EMPTY_SCOPING ? "" : (" in " + bindScoping));
limpbizkita67e39f2008-02-18 20:10:59 +000098 }
99
100 private static abstract class AbstractTarget<T> implements BindTarget<T> {
101 public void execute(ConstantBindingBuilder builder) {
102 throw new UnsupportedOperationException();
103 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000104 public T get() {
105 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000106 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000107 public Key<? extends Provider<? extends T>> getProviderKey() {
108 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000109 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000110 public Provider<? extends T> getProvider() {
111 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000112 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000113 public Key<? extends T> getKey() {
114 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000115 }
116 }
117
118 private static abstract class AbstractScoping implements BindScoping {
119 public boolean isEagerSingleton() {
120 return false;
121 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000122 public Scope getScope() {
123 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000124 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000125 public Class<? extends Annotation> getScopeAnnotation() {
126 return null;
limpbizkita67e39f2008-02-18 20:10:59 +0000127 }
128 }
129
limpbizkit3d58d6b2008-03-08 16:11:47 +0000130 BindingBuilder bindingBuilder(Binder binder) {
131 return new BindingBuilder(binder);
limpbizkita67e39f2008-02-18 20:10:59 +0000132 }
133
134 /**
135 * Package-private write access to the internal state of this command.
136 */
137 class BindingBuilder implements AnnotatedBindingBuilder<T> {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000138 private final Binder binder;
139
140 BindingBuilder(Binder binder) {
141 this.binder = binder;
142 }
143
limpbizkita67e39f2008-02-18 20:10:59 +0000144 public LinkedBindingBuilder<T> annotatedWith(
145 Class<? extends Annotation> annotationType) {
kevinb9n1601ae52008-06-03 22:21:04 +0000146 checkNotNull(annotationType, "annotationType");
limpbizkit7f8eda02008-03-26 01:11:16 +0000147 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000148 key = Key.get(key.getTypeLiteral(), annotationType);
149 return this;
150 }
151
152 public LinkedBindingBuilder<T> annotatedWith(Annotation annotation) {
kevinb9n1601ae52008-06-03 22:21:04 +0000153 checkNotNull(annotation, "annotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000154 checkNotAnnotated();
limpbizkita67e39f2008-02-18 20:10:59 +0000155 key = Key.get(key.getTypeLiteral(), annotation);
156 return this;
157 }
158
159 public ScopedBindingBuilder to(final Class<? extends T> implementation) {
160 return to(Key.get(implementation));
161 }
162
163 public ScopedBindingBuilder to(
164 final TypeLiteral<? extends T> implementation) {
165 return to(Key.get(implementation));
166 }
167
168 public ScopedBindingBuilder to(final Key<? extends T> targetKey) {
kevinb9n1601ae52008-06-03 22:21:04 +0000169 checkNotNull(targetKey, "targetKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000170 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000171 bindTarget = new AbstractTarget<T>() {
172 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
173 return linkedBindingBuilder.to(targetKey);
174 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000175 @Override public Key<? extends T> getKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000176 return targetKey;
177 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000178 public <V> V acceptVisitor(Visitor<T, V> visitor) {
179 return visitor.visitToKey(targetKey);
180 }
limpbizkita67e39f2008-02-18 20:10:59 +0000181 @Override public String toString() {
182 return String.valueOf(targetKey);
183 }
184 };
185 return this;
186 }
187
188 public void toInstance(final T instance) {
limpbizkitdf98fcd2008-06-14 05:02:15 +0000189 checkNotNull(instance, ErrorMessage.cannotBindToNullInstance().toString());
limpbizkit3d58d6b2008-03-08 16:11:47 +0000190
limpbizkit7f8eda02008-03-26 01:11:16 +0000191 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000192 bindTarget = new AbstractTarget<T>() {
193 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
194 linkedBindingBuilder.toInstance(instance);
195 return null;
196 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000197 @Override public T get() {
limpbizkita67e39f2008-02-18 20:10:59 +0000198 return instance;
199 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000200 public <V> V acceptVisitor(Visitor<T, V> visitor) {
201 return visitor.visitToInstance(instance);
202 }
limpbizkita67e39f2008-02-18 20:10:59 +0000203 @Override public String toString() {
204 return "instance " + instance;
205 }
206 };
207 }
208
209 public ScopedBindingBuilder toProvider(final Provider<? extends T> provider) {
kevinb9n1601ae52008-06-03 22:21:04 +0000210 checkNotNull(provider, "provider");
limpbizkit7f8eda02008-03-26 01:11:16 +0000211 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000212 bindTarget = new AbstractTarget<T>() {
213 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
214 return linkedBindingBuilder.toProvider(provider);
215 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000216 @Override public Provider<? extends T> getProvider() {
limpbizkita67e39f2008-02-18 20:10:59 +0000217 return provider;
218 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000219 public <V> V acceptVisitor(Visitor<T, V> visitor) {
220 return visitor.visitToProvider(provider);
221 }
limpbizkita67e39f2008-02-18 20:10:59 +0000222 @Override public String toString() {
223 return "provider " + provider;
224 }
225 };
226 return this;
227 }
228
229 public ScopedBindingBuilder toProvider(
230 Class<? extends Provider<? extends T>> providerType) {
231 return toProvider(Key.get(providerType));
232 }
233
234 public ScopedBindingBuilder toProvider(
235 final Key<? extends Provider<? extends T>> providerKey) {
kevinb9n1601ae52008-06-03 22:21:04 +0000236 checkNotNull(providerKey, "providerKey");
limpbizkit7f8eda02008-03-26 01:11:16 +0000237 checkNotTargetted();
limpbizkita67e39f2008-02-18 20:10:59 +0000238 bindTarget = new AbstractTarget<T>() {
239 public ScopedBindingBuilder execute(LinkedBindingBuilder<T> linkedBindingBuilder) {
240 return linkedBindingBuilder.toProvider(providerKey);
241 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000242 @Override public Key<? extends Provider<? extends T>> getProviderKey() {
limpbizkita67e39f2008-02-18 20:10:59 +0000243 return providerKey;
244 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000245 public <V> V acceptVisitor(Visitor<T, V> visitor) {
246 return visitor.visitToProviderKey(providerKey);
247 }
limpbizkita67e39f2008-02-18 20:10:59 +0000248 @Override public String toString() {
249 return "provider " + providerKey;
250 }
251 };
252 return this;
253 }
254
255 public void in(final Class<? extends Annotation> scopeAnnotation) {
kevinb9n1601ae52008-06-03 22:21:04 +0000256 checkNotNull(scopeAnnotation, "scopeAnnotation");
limpbizkit7f8eda02008-03-26 01:11:16 +0000257 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000258
259 bindScoping = new AbstractScoping() {
260 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
261 scopedBindingBuilder.in(scopeAnnotation);
262 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000263 @Override public Class<? extends Annotation> getScopeAnnotation() {
limpbizkita67e39f2008-02-18 20:10:59 +0000264 return scopeAnnotation;
265 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000266 public <V> V acceptVisitor(Visitor<V> visitor) {
267 return visitor.visitScopeAnnotation(scopeAnnotation);
268 }
limpbizkita67e39f2008-02-18 20:10:59 +0000269 @Override public String toString() {
270 return scopeAnnotation.getName();
271 }
272 };
273 }
274
275 public void in(final Scope scope) {
kevinb9n1601ae52008-06-03 22:21:04 +0000276 checkNotNull(scope, "scope");
limpbizkit7f8eda02008-03-26 01:11:16 +0000277 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000278 bindScoping = new AbstractScoping() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000279
limpbizkita67e39f2008-02-18 20:10:59 +0000280 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
281 scopedBindingBuilder.in(scope);
282 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000283 @Override public Scope getScope() {
limpbizkita67e39f2008-02-18 20:10:59 +0000284 return scope;
285 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000286 public <V> V acceptVisitor(Visitor<V> visitor) {
287 return visitor.visitScope(scope);
288 }
limpbizkita67e39f2008-02-18 20:10:59 +0000289 @Override public String toString() {
290 return String.valueOf(scope);
291 }
292 };
293 }
294
295 public void asEagerSingleton() {
limpbizkit7f8eda02008-03-26 01:11:16 +0000296 checkNotScoped();
limpbizkita67e39f2008-02-18 20:10:59 +0000297 bindScoping = new AbstractScoping() {
298 public void execute(ScopedBindingBuilder scopedBindingBuilder) {
299 scopedBindingBuilder.asEagerSingleton();
300 }
301 @Override public boolean isEagerSingleton() {
302 return true;
303 }
limpbizkit3d58d6b2008-03-08 16:11:47 +0000304 public <V> V acceptVisitor(Visitor<V> visitor) {
305 return visitor.visitEagerSingleton();
306 }
limpbizkita67e39f2008-02-18 20:10:59 +0000307 @Override public String toString() {
308 return "eager singleton";
309 }
310 };
311 }
312
limpbizkit3d58d6b2008-03-08 16:11:47 +0000313 static final String IMPLEMENTATION_ALREADY_SET
314 = "Implementation is set more than once.";
315 static final String SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not"
316 + " permitted when binding to a single instance.";
317 static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
318 static final String ANNOTATION_ALREADY_SPECIFIED = "More than one annotation"
319 + " is specified for this binding.";
320
limpbizkit7f8eda02008-03-26 01:11:16 +0000321 private void checkNotTargetted() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000322 if (bindTarget != EMPTY_BIND_TARGET) {
323 binder.addError(IMPLEMENTATION_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000324 }
325 }
326
limpbizkit7f8eda02008-03-26 01:11:16 +0000327 private void checkNotAnnotated() {
limpbizkita67e39f2008-02-18 20:10:59 +0000328 if (BindCommand.this.key.getAnnotationType() != null) {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000329 binder.addError(ANNOTATION_ALREADY_SPECIFIED);
limpbizkita67e39f2008-02-18 20:10:59 +0000330 }
331 }
332
limpbizkit7f8eda02008-03-26 01:11:16 +0000333 private void checkNotScoped() {
limpbizkit3d58d6b2008-03-08 16:11:47 +0000334 // Scoping isn't allowed when we have only one instance.
335 if (bindTarget.get() != null) {
336 binder.addError(SINGLE_INSTANCE_AND_SCOPE);
337 return;
338 }
339
340 if (bindScoping != EMPTY_SCOPING) {
341 binder.addError(SCOPE_ALREADY_SET);
limpbizkita67e39f2008-02-18 20:10:59 +0000342 }
343 }
limpbizkitd6967b92008-05-16 15:28:51 +0000344
345 @Override public String toString() {
346 String type = key.getAnnotationType() == null
347 ? "AnnotatedBindingBuilder<"
348 : "LinkedBindingBuilder<";
349 return type + key.getTypeLiteral() + ">";
350 }
limpbizkita67e39f2008-02-18 20:10:59 +0000351 }
352}